比较C++中move和智能指针的习惯?

Compare the habits between move and smart pointer in C++?

在C++11/14中,对象可以通过move或smark指针来传递。

(1) 这是 move 的示例:

class MoveClass {
private:
    int *tab_;
    int alloc_;
    void Reset() {
        tab_ = nullptr;
        alloc_ = 0;
    }
    void Release() {
        if (tab_) delete[] tab_;
        tab_ = nullptr;
        alloc_ = 0;
    }

public:
    MoveClass() : tab_(nullptr), alloc_(0) {}
    ~MoveClass() {
        Release();
    }
    MoveClass(MoveClass && other) : tab_( other.tab_ ), alloc_( other.alloc_ ) {
        other.Reset();
    }
    MoveClass & operator=(MoveClass && other) {
        if (this == &other) return *this;
        std::swap(tab_, other.tab_);
        std::swap(alloc_, other.alloc_);
        return *this;
    }
    void DoSomething() { /*...*/ }
};

当我们使用这个可移动的MoveClass时,我们可以这样写代码:

int main() {
    MoveClass a;
    a.DoSomething();  // now a has some memory resource
    MoveClass b = std::move(a);  // move a to b
    return 0;
}

总是写move-constructor/move-operator=很无聊,有时候用shared_ptr/unique_ptr也是一样的效果,到处都是java,reference/pointer

(2) 下面是例子:

class NoMoveClass {
private:
    int *tab_;
    int alloc_;
    void Release() {
        if (tab_) delete[] tab_;
        tab_ = nullptr;
        alloc_ = 0;
    }

public:
    NoMoveClass() : tab_(nullptr), alloc_(0) {}
    ~NoMoveClass() {
        Release();
    }
    MoveClass(MoveClass && other) = delete;
    MoveClass & operator=(MoveClass && other) = delete;
    void DoSomething() { /*...*/ }
};

我们可以这样使用:

int main() {
    std::shared_ptr<NoMoveClass> a(new NoMoveClass());
    a->DoSomething();
    std::shared_ptr<NoMoveClass> b = a; // also move a to b by copy pointer.
    return 0;
}

总是使用第2个是个好习惯吗?

为什么很多库,STL使用第一个,而不是第一个?

Always write move-constructor/move-operator= is boring

几乎从不需要编写自己的移动constructor/assignment,因为(正如您提到的)C++ 为您提供了许多基本的资源管理器——智能指针,容器、智能锁等

通过依赖 class 中的那些 ,您可以启用默认的移动操作,从而实现最小的代码大小和正确的语义:

class MoveClass {
private:
    std::vector<int> data;
public:
    void DoSomething() { /*...*/ }
};

现在您可以像在 (1) 中那样使用您的 class 或作为其他 classes 中的成员,您可以确定它有移动语义,你用尽可能少的代码完成了它。

关键是通常只需要为最低级别的 classes 实现移动操作,这些可能已经被 STL 覆盖,或者如果需要一些奇怪的特定行为 - 这两种情况都应该是罕见且不会导致 "Always writing move-constructor/move-operator=".

另请注意,虽然方法 (1) 不必要地冗长,但 (2) 是不可接受的 - 你有一个资源管理 class 没有完成它的工作,因此您必须将它包装在代码中各处的智能指针中,使其更难理解并最终导致比 (1)[ 更多的代码=28=]