我什么时候需要交换功能?

时间:2016-06-20 19:38:10

标签: c++ c++11

当我编写保存资源的类时,我非常习惯于编写简单的交换函数来简化传输/复制资源的过程。以下代码是一个简单的例子:

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函数,还是只是浪费代码?

1 个答案:

答案 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。