在C++中避免别名(例如,将容器的元素添加到自身时)的"right"方法是什么?
What is the "right" way to avoid Aliasing (e.g. when adding an element of a container to itself) in C++?
std::vector<int> a;
a.push_back(1);
a.push_back(a[0]);
我刚刚了解到上面的代码可能非常危险。
(如果原因不明显,你并不孤单...这对我来说也不明显。
我的问题:
处理它的"标准"方式是什么?创建一个新变量,然后立即将其分配给某些东西,这对我来说似乎有点奇怪。有没有更好的处理方法?
您如何训练自己注意这样的混叠问题?您在寻找什么模式?我不知道承认这种情况;当我了解 C 中的
restrict
关键字时,我才知道混叠,直到现在我才明白问题到底是什么。
编辑:
我很想接受一个答案,但似乎问题的第 (2( 部分没有得到回答。我想知道人们使用什么策略来定位他们编写的代码中的别名错误。
到目前为止,我想出的一种策略是避免在两个参数中传递相同的值。(在本例中,一个参数是隐式的,一个是显式的。
还有其他容易注意和注意的事情吗?
编辑:从技术上讲,如果包含的类型具有无抛掷复制构造函数,则标准并不要求这是正确的。我不知道任何实现无论如何都不成立,因为当通用的实现在所有情况下都同样有效时,它需要生成两个push_back
实现。
混叠通常是一个问题,但在这种特殊情况下不是。代码:
assert( v.size() > 0 );
v.push_back( v[0] );
通过异常保证(这是不实现自己的容器的一个很好的理由,你可能不会正确实现它们(,通过标准(C++03(保证是正确的。特别是§23.1 [lib.container.requirements]/10指令:
除非另有说明(见23.2.1.3和23.2.4.3([注:这两个参考文献分别指
deque
和vector
insert
]本条款中定义的所有容器类型都满足以下附加要求:— 如果 push_back(( 或 push_front(( 函数抛出异常,则该函数没有影响。
其中重要的一点是,如果在操作中抛出任何异常,容器保持不变,这意味着没有迭代器失效,这反过来意味着原始内存区域保持不变,直到保证不会抛出任何异常(析构函数的双关语除外(。由于在一般情况下,复制构造函数可以抛出,因此实现必须确保在销毁任何对象之前执行所有副本。
这在 C++0x 中变得更加明显,当对象不是从一个位置复制到另一个位置,而是移动时。由于新元素的副本可能会抛出,因此必须在执行任何移动之前执行该副本,否则您将处于原始容器中的某些对象已失效的情况。
我想这将是安全的:
std::vector<int> a(1);
a.push_back(1);
a.push_back(int(a[0]));
在push_back(const T& el);
实现中检查el
是否在数组或其他内部存储器中。这是处理此类问题的唯一政治正确方法。
应将其作为不同的容器处理 - 不同的安全规则。
这对您来说可能不是一个有用的答案,但恕我直言,"正确"的方法是容器类应该正确处理别名,这样调用者就不必担心它。 特别是,push_back(((或等效的(应执行以下操作:
// C++-ish pseudo-code, exception-safety left as an exercise for the reader
void push_back(const T & t)
{
if (current_size == alloced_size)
{
// Oops, our data array is full. Time to trade it in for a bigger one
T * newArray = new T[alloced_size*2];
copy_items(newArray, current_array, current_size);
newArray[current_size++] = t;
delete [] current_array; // delete old array only AFTER all references to t
current_array = new_array;
alloced_size *= 2;
}
else current_array[current_size++] = t;
}
我只是在飞翔,所以请不要认为这是福音,但这行得通吗?
a.push_back(1);
a.push_back(&(new int(a[0])));
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- C++中名称篡改的目的是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 是什么阻止DOMTimerCoordinator::NextID进入无休止的循环
- 派生类销毁的最佳实践是什么
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 通过JNI传递数据数组的最快方法是什么
- "using namespace std;"在C++的作用是什么?
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 文件系统:复制功能的速度秘诀是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么