我看过copy-and-swap various中推荐的成语places作为为赋值运算符实现强异常安全性的推荐/最佳/唯一方法。在我看来,这种方法也有缺点。
考虑以下使用 copy-and-swap 的简化类 vector 类:
class IntVec {
size_t size;
int* vec;
public:
IntVec()
: size(0),
vec(0)
{}
IntVec(IntVec const& other)
: size(other.size),
vec(size? new int[size] : 0)
{
std::copy(other.vec, other.vec + size, vec);
}
void swap(IntVec& other) {
using std::swap;
swap(size, other.size);
swap(vec, other.vec);
}
IntVec& operator=(IntVec that) {
swap(that);
return *this;
}
//~IntVec() and other functions ...
}
通过复制构造函数实现分配可能是高效的并保证异常安全,但它也可能导致不必要的分配,甚至可能导致未调用的内存不足错误。
考虑在具有 <2GB 堆限制的机器上将 700MB IntVec
分配给 1GB IntVec
的情况。最佳分配将意识到它已经分配了足够的内存,并且只将数据复制到已经分配的缓冲区中。 copy-and-swap 实现将导致在释放 1GB 缓冲区之前分配另一个 700MB 缓冲区,导致所有 3 个缓冲区同时尝试在内存中共存,这将不必要地引发内存不足错误。
这个实现可以解决问题:
IntVec& operator=(IntVec const& that) {
if(that.size <= size) {
size = that.size;
std::copy(that.vec, that.vec + that.size, vec);
} else
swap(IntVec(that));
return *this;
}
所以底线是:
我是对的,这是 copy-and-swap 习语的问题,还是正常的编译器优化以某种方式消除了额外的分配,或者我忽略了 copy-and-swap 解决的“更好”版本的一些问题,还是我的数学/算法做错了,问题并不存在?
最佳答案
实现重用空间有两个问题
如果您分配
very_huge_vect = very_small_vect;
额外的内存将不会被释放。这可能是您想要的,也可能不是。如果是整数,一切都很好,但是复制操作可能会抛出异常的对象呢?您最终会得到一个困惑的数组,其中部分拷贝已完成并且已被截断。如果复制操作失败(交换习语的作用),最好保持目标不变。
顺便说一句,作为一般规则,在极少数情况下,您可以找到看起来“始终是最佳解决方案”的任何东西。如果您正在寻找 Elixir ,那么编程将不是正确的地方。
关于c++ - copy-and-swap 总是最好的解决方案吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23918469/