C++中的六个特殊的成员函数,称为“六大”:
- 默认构造函数:X()
- 拷贝构造函数:X(const X&)
- 拷贝赋值运算符:operator=(const X&)
- 移动构造函数:X(X&&)
- 移动赋值运算符:operator=(X&&)
- 析构函数:~X()
默认构造函数的声明:默认构造函数可以在没有参数的情况下被调用,但它可能每个参数都有默认值。
如果能避免定义默认操作,那就这么做
这一规则也被称为“零法则”。这意味着你可以通过使用有合适的拷贝/移动语义的类型,来避免自行编写构造函数、拷贝/移动构造函数、赋值运算符和析构函数。有合适的拷贝/移动语义的类型包括规范类型,如内置类型bool或double,也包括标准模板库(STL)的容器,如std::vector或std::string
class Named_map
{
public:
// 没有声明任何默认操作
private:
std::string name;
std::map<int, int> rep;
};
Named_map nm; //默认构造
Named_map nm2 {nm}; //拷贝构造
默认构造和拷贝构造之所以有效,是因为std::string和std::map已经定义了相应的操作。当编译器为一个类自动生成拷贝构造函数时,它调用该类的所有成员和所有基类的拷贝构造函数。
如果定义或=delete了任何默认操作,就对所有默认操作都进行定义或=delete
用户声明 | 默认构造函数 | 析构函数 | 拷贝构造函数 | 拷贝赋值 | 移动构造函数 | 移动赋值 | |
全部不声明 | 预置 | 预置 | 预置 | 预置 | 预置 | 预置 | |
任意构造函数 | 不声明 | 预置 | 预置 | 预置 | 预置 | 预置 | |
默认构造函数 | 用户声明 | 预置 | 预置 | 预置 | 预置 | 预置 | |
析构函数 | 预置 | 用户声明 | 预置 | 预置 | 不声明 | 不声明 | |
拷贝构造函数 | 不声明 | 预置 | 用户声明 | 预置 | 不声明 | 不声明 | |
拷贝赋值 | 预置 | 预置 | 预置 | 用户声明 | 不声明 | 不声明 | |
移动构造函数 | 不声明 | 预置 | 弃置 | 弃置 | 用户声明 | 不声明 | |
移动赋值 | 预置 | 预置 | 弃置 | 弃置 | 不声明 | 用户声明 |
首先,“用户声明”是指对于这六个特殊成员函数中的某一个,你明确地给出了定义,或用=default请求编译器给出预置定义。用=delete删除特殊成员函数成员函数的操作也被认为进行了定义。从本质上讲,当你只是使用名字,比如默认构造函数的名字时,这也算作用户声明。
当你定义任何构造函数时,默认构造函数就没有了。默认构造函数是可以在没有参数的情况下调用的构造函数。
当你用=default或=delete定义或删除默认构造函数时,其他特殊成员函数都不受影响。
当你用=default或=delete定义或删除析构函数、拷贝构造函数或拷贝赋值操作符时,编译器不会生成移动构造函数和移动赋值运算符。这意味着移动构造或移动赋值这样的移动操作会退回到拷贝构造或拷贝赋值。
当用=default或=delete定义或删除移动构造函数或移动赋值运算符时,只能得到定义的=default或=delete的移动构造函数或移动赋值运算符。后果是,拷贝构造函数和拷贝赋值运算符被设为=delete。因此,调用一个拷贝操作,如拷贝构造或拷贝赋值,将导致编译错误