C++:如果我不使用移动,我还需要关心复制控制吗?

C++: Need I still care copy control if I don't use move?

复制控制似乎与默认构造函数、复制构造函数、复制赋值运算符、析构函数等有关,乘以用户编写的和合成的,加上移动复制/赋值。

听起来很复杂。我发现很难记住什么时候合成什么。

只是想知道,如果我不使用 move in constructor 或 assignment operator,我还需要关心它们之间的区别吗?

在用户代码中,您几乎不需要编写析构函数,因此 copy/move 构造函数和赋值运算符。这是因为您将组成 classes 个支持 RAII 的对象。

我们唯一需要编写析构函数的情况是我们正在控制非 RAII 资源,例如从 C 风格或原始 C++ 库导出的文件句柄或数据库连接。

在这种情况下,我们只需将资源包装在 unique_ptr(使用自定义删除器)或 shared_ptr(同样,使用自定义删除器)中,我们就完成了。

这给我们留下了 2 个场景:

  1. 多态接口的基础 class(不会被 shared_ptr 控制)——在这种情况下,我们必须编写一个虚拟析构函数,然后可能实现 move/copy 就默认实现而言。

  2. A class 是可移动的(因为它拥有一个 unique_ptr,比方说?)并且我们希望它是可复制的。现在我们被迫实现复制操作和默认移动操作。

还有一些其他极端情况,例如如果您的 class 拥有一个不可复制的互斥体。但是,如果您的 class 拥有互斥锁,则要求它可复制可能已经是一个设计错误。无论如何,这时候你应该已经记住了 copy/assignment/move 规则。

一些例子:

struct owns_a_file_movable
{
    struct file_deleter {
        void operator()(FILE* f) const noexcept {
            fclose(f);
        }
    };

    // RAII class now, moveable. Copy would not make sense.
    std::unique_ptr<FILE, file_deleter> file_;
};

struct owns_a_file_copyable
{
    struct file_deleter {
        void operator()(FILE* f) const noexcept {
            fclose(f);
        }
    };

    // construct from string
    owns_a_file_copyable(std::string fname)
    : path_(std::move(fname))
    , file_(fopen(path_, "r"), file_deleter())
    {
    }

    // we want it to be copyable. In our case, a copy will open another
    // instance of the file. so we must store the filename.

    owns_a_file_copyable(owns_a_file_copyable const& r)
    : path_(t.path_)
    , file_(fopen(path_, "r"), file_deleter())
    {}

    owns_a_file_copyable& operator=(owns_a_file_copyable const& r)
    {
        auto tmp = r;
        std::swap(path_, tmp.path_);   // changed: was r.path_ which was wrong
        std::swap(file_, tmp.file_);
        return *this;
    }

    owns_a_file_copyable(owns_a_file_copyable&&) = default;
    owns_a_file_copyable& operator=(owns_a_file_copyable&&) = default;


    // std::string is fully RAII
    std::string path_;

    // RAII class now, moveable
    std::unique_ptr<FILE, file_deleter> file_;
};

struct how_most_classes_should_be 
{
    // no destructors, copy operators, assignment or move - it's all 
    // generated for you.

    std::string this_;
    std::string that_;
    std::shared_ptr<const OtherThing> shared_other_; // note - shared semantics
    std::function<void()> closure_;   // these are copyable too
};

阿索斯

什么触发了什么?

// ordinary constructor
auto y = owns_a_file_copyable("/users/rhodges/foo.txt");

// copy constructor: owns_a_file_copyable(owns_a_file_copyable const&)
auto x = y;

// copy assignment: owns_a_file_copyable& operator-(owns_a_file_copyable const&)
x = y

// move-constructor: owns_a_file_copyable(owns_a_file_copyable &&)
auto z = std::move(x);

// move-assignment: owns_a_file_copyable& operator-(owns_a_file_copyable &&)
z = std::move(y);

// also move-constructor
extern owns_a_file_copyable make_file();
auto w = make_file();

// this time move-assignment
w = make_file();