当我编写保存资源的类时,我非常习惯于编写简单的交换函数来简化传输/复制资源的过程。以下代码是一个简单的例子:
class MyArray {
public:
MyArray(size_t size) :
_array(size ? new int[size] : nullptr),
_size(size)
{
}
MyArray(const MyArray & mv) :
MyArray(mv._size)
{
if(mv._size) std::copy(mv._array, mv._array + mv._size, _array);
}
static void friend swap(MyArray & mv1, MyArray & mv2) noexcept {
std::swap(mv1._array, mv2._array);
std::swap(mv1._size, mv2._size);
}
MyArray(MyArray && mv) {
swap(*this, mv);
}
MyArray & operator=(MyArray mv) {
swap(*this, mv);
return *this;
}
~MyArray() noexcept {
delete[] _array;
}
int & operator[](size_t index) {
if(index >= _size) throw std::exception("Index Out of Bounds!");
return _array[index];
}
const int & operator[](size_t index) const {
if(index >= _size) throw std::exception("Index Out of Bounds!");
return _array[index];
}
size_t size() const {
return _size;
}
private:
int * _array;
size_t _size;
};
但是,我本可以选择[等效]实现相同的代码:
class MyArray {
public:
MyArray(size_t size) :
_array(size)
{
}
int & operator[](size_t size) {
if(index >= size()) throw std::exception("Index Out of Bounds!");
return _array[index];
}
const int & operator[](size_t index) const {
if(index >= size()) throw std::exception("Index Out of Bounds!");
return _array[index];
}
size_t size() const {
return _array.size();
}
private:
std::vector<int> _array;
};
在第二个代码中,swap
功能默认为正常的std::swap
行为(即,将第一个移植到临时,将第二个移动到第一个,并将第二个移动到第一个,并移动 - 分配暂时进入第二个),它应该与第一个代码相同(在设计级别上)。
我遇到的问题是我必须明确定义swap
函数的情况,但并不是我只是试图重用的情况代码(比如第一个示例代码)?是否存在明显(或不那么明显)的场景,即使我的代码中没有任何内容明确地管理资源,我也会客观地受益于swap
函数?我应该为我编写的每个[move-constructable]类自动编写swap
函数,还是只是浪费代码?
答案 0 :(得分:5)
如果您没有定义自己的交换操作,using std::swap; swap(lhs, rhs);
大致会编译:
auto tmp = std::move(rhs);
rhs = std::move(lhs);
lhs = std::move(tmp);
这导致发生3次移动,并且需要竞争临时对象存在。
如果移动赋值和移动构造运算符具有强异常保证,则此交换具有弱异常保证。如果移动赋值和移动构造运算符不是,则此交换也是如此。
相比之下,成员交换可以使对象处于非相干状态。如果您的代码可以处理此问题,则意味着您永远不必创建完整的临时对象。因此,如果您可以处理部分交换的组件,并且完整的临时对象(从移动创建)有一些成本,那么成员交换可能会更快。
但是,如果所有内容都不是(通常是交换/移动),则生成的交换操作在逻辑上是等效的。
您的第二个MyArray
实际上并未管理资源。这是由std::vector
处理的,它在你的第一个代码中编写了大部分代码。
Zero的规则是,不管理资源的类不需要编写任何复制或移动赋值或构造,也不需要编写析构函数。这类可能也不需要交换。
执行写入复制/移动和销毁的类,管理资源的类,可以使用swap来执行此操作。否则,std::swap
做得不错。
顺便说一句,如果他们确实使用swap,并且交换只是成员,请写下:
friend auto mytie( MyArray& self ) noexcept {
return std::tie(self.array, self.size);
}
然后交换变为:
friend void swap( MyArray& lhs, MyArray& rhs ) noexcept {
std::swap( mytie(lhs), mytie(rhs) );
}
减少了你的重复会员名称&#34;样板数量为1。