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

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

本文关键字:关心 复制 控制 如果 move C++      更新时间:2023-10-16

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

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

只是想知道,如果我在构造函数或赋值操作符中不使用move,我还需要关心差异吗?

在用户代码中,几乎不需要编写析构函数,从而复制/移动构造函数和赋值操作符。这是因为您将组成支持raii的对象的类。

只有在控制非raii资源时才需要编写析构函数,例如从c风格或原始c++库导出的文件句柄或数据库连接。

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

那么我们还有两种情况:

  1. 多态接口的基类(不受shared_ptr控制)——在这种情况下,我们必须编写一个虚析构函数,然后可能根据默认实现实现move/copy。

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

还有一些其他的极端情况,比如你的类拥有一个不可复制的互斥锁。但是,如果您的类拥有互斥锁,那么要求互斥锁是可复制的可能已经是一个设计错误。无论如何,此时你应该已经熟记了复制/赋值/移动规则。

一些例子:

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();