17 理解特殊成员函数的生成

17 理解特殊成员函数的生成

​ 移动构造和拷贝构造是编译器默认生成的内容,虽然移动构造在某些成员身上表现为拷贝构造。

生成规则

拷贝构造

​ 两个拷贝构造之间是相互独立的:声明一个不会限制编译器生成另一个。所以如果你声明一个拷贝构造函数,但是没有声明拷贝赋值运算符,如果写的代码用到了拷贝赋值,编译器会帮助你生成拷贝赋值运算符。

移动构造

​ 两个移动操作不是相互独立的。如果你声明了其中一个,编译器就不再生成另一个。

关联互斥

​ 如果一个类显式声明了拷贝操作,编译器就不会生成移动操作。

​ 声明移动操作(构造或赋值)也使得编译器禁用拷贝操作。

Rule_Of_Three规则

​ 这个规则告诉我们:如果你声明了拷贝构造函数,拷贝赋值运算符,或者析构函数三者之一,你应该也声明其余两个。

​ 由此出现了推理:当出现了析构函数,会影响移动构造函数的默认生成。(但不影响拷贝操作)

​ 由于Rule_Of_Three规则以及 拷贝与移动之间的默认互斥,所以仅当下面条件成立时才会自动生成移动操作(当需要时):

  • 类中没有拷贝操作

  • 类中没有移动操作

  • 类中没有用户定义的析构

尽可能使用default代替隐式生成

​ 由于Rule_Of_Three规则,因此尽可能为特殊成员函数使用default代替隐式生成。因为开始时开发的默认隐式生成的共识,可能会被后续开发添加的析构函数而打破。

特殊成员函数的生成规则

  • 默认构造函数:和C++98规则相同。仅当类不存在用户声明的构造函数时才自动生成。

  • 析构函数:基本上和C98相同;稍微不同的是现在析构默认noexcept(参见Item14)。和C98一样,仅当基类析构为虚函数时该类析构才为虚函数。

  • 拷贝构造函数:和C++98运行时行为一样:逐成员拷贝non-static数据。仅当类没有用户定义的拷贝构造时才生成。如果类声明了移动操作它就是delete的。当用户声明了拷贝赋值或者析构,该函数自动生成已被废弃。

  • 拷贝赋值运算符:和C++98运行时行为一样:逐成员拷贝赋值non-static数据。仅当类没有用户定义的拷贝赋值时才生成。如果类声明了移动操作它就是delete的。当用户声明了拷贝构造或者析构,该函数自动生成已被废弃。

  • 移动构造函数移动赋值运算符:都对非static数据执行逐成员移动。仅当类没有用户定义的拷贝操作,移动操作或析构时才自动生成。

边缘规则:函数模板不会阻止编译器生成特殊成员函数

​ 由于标准中没有这种规则:函数模板阻止编译器生成特殊成员函数。因此,下面的代码,编译器仍然会生成移动和拷贝构造,即使模板会产出拷贝构造和拷贝运算符的函数签名:

class Widget {
// ...
template<typename T> //从任何东西构造Widget
Widget(const T& rhs);

template<typename T> //从任何东西赋值给Widget
Widget& operator=(const T& rhs);
// ...
};