我什么时候需要交换功能

When do I need a swap function?

本文关键字:交换 功能 什么时候      更新时间:2023-10-16

当我编写拥有资源的类时,我非常习惯编写简单的交换功能来简化传输/复制资源的过程。以下代码是一个微不足道的示例:

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-Constructtructs]类自动使用swap函数,还是可以通过此操作来浪费代码?

如果您不定义自己的交换操作,则using std::swap; swap(lhs, rhs);将其编译为大致:

auto tmp = std::move(rhs);
rhs = std::move(lhs);
lhs = std::move(tmp);

这会导致3个动作发生,并且需要一个竞争的临时对象。

如果移动分配和移动构造运算符具有强大的异常保证,则此交换的异常保证弱。如果移动分配和移动构造运算符是不可以的,那么此交换也是如此。

相比之下,会员互换可以在不一致的状态下留下对象。如果您的代码可以处理此操作,则意味着您永远不必创建完整的临时对象。因此,如果您可以处理部分交换的组件,并且完整的临时对象(由移动创建)有一定的成本,那么会员交换的速度可能更快。

但是,如果一切都不是(通常是互换/移动),则结果交换操作在逻辑上等效。


您的第二个MyArray实际上没有管理资源。这是由std::vector处理的,它确实在您的第一个代码中写了大部分代码。

零的规则是,不管理资源的类不需要编写任何副本或移动分配或施工,也不需要驱动器。这样的课程可能也不需要交换。

确实写副本/移动和破坏的类,管理资源的类,可以使用交换来做到这一点。否则, std::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) );
}

将您的"重复成员名称"的样板计数减少1。