为什么插入用户定义的析构函数需要用户定义的复制构造函数

Why does the insertion of user defined destructor require an user defined copy constructor

编译以下代码:

#include <vector>
#include <iostream>
#include <memory>

using namespace std;

class container
{
public:
    container(){}
    ~container(){}
};

class Ship
{
public:
    Ship(){}
    //Ship(const Ship & other){cout<<"COPY"<<endl;}
    //~Ship(){}

    std::unique_ptr<container> up;
};

Ship buildShip()
{
    Ship tmp;
    return tmp;
}

int main(int argc, char *argv[])
{
    return 0;
}

但是,如果我们包含用户定义的析构函数 ~Ship(){},则只有当我们还包含用户定义的复制构造函数 Ship(const Ship & other){cout<<"COPY"<<endl;}

时,代码才会编译

简而言之:

编译:

Ship(){}
//Ship(const Ship & other){cout<<"COPY"<<endl;}
//~Ship(){}

编译:

Ship(){}
Ship(const Ship & other){cout<<"COPY"<<endl;}
~Ship(){}

不编译:

Ship(){}
//Ship(const Ship & other){cout<<"COPY"<<endl;}
~Ship(){}

为什么插入用户定义的析构函数需要一个用户定义的复制构造函数,为什么我们在上面的例子中根本需要一个复制构造函数?

我希望上面的示例中不需要复制构造函数,因为 unique_ptr 甚至无法复制。

想法是,如果编译器生成的析构函数对您的 class 来说不够好,那么复制构造函数和复制赋值运算符也可能不够好,因此编译器 可能删除那些复制操作的隐式实现。从技术上讲,即使您有用户定义的析构函数,编译器仍可能为您提供隐式复制操作,但这种行为在 c++11 中已被弃用。

Rule of Three

AFAIK,您仍然需要一个复制构造函数,因为 buildShip() returns 按值计算。

(但是,有趣的是您能够编译使用隐式复制构造函数的版本。由于 unique_ptr 成员,您不应该这样做...)

代码被编译是因为 buildShip() 会在返回 tmp 时使用编译器自动生成的移动构造函数。添加用户声明的析构函数可防止编译器自动生成析构函数。例如,参见 or 个问题。并且编译器生成的拷贝构造函数不能使用,因为成员upstd::unique_ptrunique_ptr 的复制构造函数被显式删除。

所以这将编译,因为明确要求编译器生成移动构造函数:

class Ship
{
public:
    Ship(){}
    Ship(Ship&&) = default;
    ~Ship(){}
    std::unique_ptr<container> up;
};