[TOC]
C++ 默认生成的特殊函数
前言
再度感叹 C++ 历史包袱有点重,为了兼容旧代码导致细节的繁杂。
为了撰文的方便,constructor, assignment operator, destructor 分别由 ctor, assignment, dtor 替代。
C++ 11 以前
Rule of Three (Big Three)
Big Three 含义:C++11 前,如果手动定义了以下三个函数之一或者更多,那么应尽可能手动定义所有三个。这只是一个大家都认为比较好的约定准则,并没有被写入标准。
- copy ctor
- copy assignment
- destructor
这三个函数之所以特殊,是因为涉及到 处理资源的生命周期,编译器认为,既然程序员显示定义了某个特殊函数,这表明这个类的资源管理有点复杂(non-trivial),这超出了编译器自动处理能力的范围,程序员理应编写涉及资源管理的其他函数。
C++11 前默认生成的函数
- default ctor
- destructor
- copy ctor
- copy assignment
These functions are known as the special member functions, and they are what make simple user-defined types in C++ behave like structures do in C.
编译器自动生成这些函数,是为了使得 C++ 里用户自定义的类型能够像 C 里面的类型一样地表现。
比如:
int a = 2; // in c code
TypeA a;
TypeA aa = a; // in C++ code.
值得一提的是 default ctor,default ctor 只初始化资源,在语义上更接近于任何其他取值的构造函数(也称为 initialization ctor),而不是那些处理资源生命周期的特殊函数。这也是为什么其他三个函数称为 big three。
不好的习惯:尽管有 Rule of Three,但是 C++98 并不强制执行。
C++98 didn’t natively enforce the rule of 3.
具体来说:
- 如果有自定义了某个函数,那么编译器不会生成对应函数,很容易理解。比如明确定义了一个 ctor,那么就不会自动生成 default ctor。同理 dtor, copy ctor, copy assigment.
- 如果自定义了任何 ctor,则不会自动生成 default ctor。
- 如果自定义了 dtor,则不会自动生成 default dtor。
- 但对于copy function 而言
- copy ctor: 不论你是否写了 initialization ctor 或者 copy assignment, dtor,都会默认生成 ctor。
- copy assignment : 不论你是否写了 initialization ctor 或者 copy assignment, dtor,都会默认生成(因为标准不强制执行 Rule of 3)。
言下之意:C++11 前,dtor, copy ctor, copy assignment 只要用户不定义他们,编译器就会自动生成一个。而如果自定义了任何 ctor,则不会自动生成 default ctor。 但是,C++11 后,规则有所改变,见后文。
Although considered good practice, the compiler can’t enforce it. The C++ standard [ N1316 ] mandates that implicit versions will be created if a user doesn’t declare them explicitly:
§ 12.4 / 3 If a class has no user-declared destructor, a destructor is declared implicitly
§ 12.8 / 4 If the class definition does not explicitly declare a copy constructor, one is declared implicitly.
§ 12.8 / 10 If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly
步入 C++11
C++11 引入了 移动语义,加入了 move ctor 和 move assignment。
Rule of Five
Rule of Five: 含义:C++11 以来,如果手动定义了以下五个函数之一或者更多,那么应尽可能手动定义全部五个。
- copy ctor
- copy assignment
- destructor
- move ctor
- move assignment
C++11 及其以后 默认生成的函数
C++11 引入了 =default 显示表示编译器默认生成的函数,后文虽把 “标记为=default”、“编译器默认生成” 分成两种说法,是为了易于理解,本质上一回事。
尽管 C++98 没有强制推行 Rule of Three, 而 C++11 又想强制推行 Rule of Five(后面又提倡 Rule of Zero),但是又得考虑兼容以前的旧代码,混乱由此而生。
具体来说:
- 如果有自定义了某个函数,那么编译器不会生成对应函数,很容易理解。比如明确定义了一个 ctor,那么就不会自动生成 default ctor。同理 dtor, copy ctor, copy assigment.
如果自定义了任何 ctor,则不会自动生成 default ctor。
如果自定义了 dtor,则不会自动生成 default dtor。
对于 copy function(包括 copy ctor 和 copy assignment) 而言,如果没有 move ctor,move assignment ,这种情况下将自动生成 copy function,因为编译器认为这是 C++11 以前的代码,那么为了兼容,就得自动生成 copy ctor 和 copy assignment。具体规则如下:
- 3.1 对于 copy ctor: 为了兼容,
- 如果用户没有自定义 copy ctor,先考虑自动生成一个默认的,(后续可能被删除/标记为
=delete) - 如果用户自定义了 move function,那么将之前生成的标记为
=delete;如果没有 move function,标记为=default, - 但如果用户自定义了 copy assignment / dtor,则还是会生成一个默认的,但是这个特性是被弃用的,deprecated. 会报 warning,未来这个特性会被删除。
- 如果用户没有自定义 copy ctor,先考虑自动生成一个默认的,(后续可能被删除/标记为
- 3.2 copy assignment 同理。
C++11 [ N3242 ] added wording to the Standard, deprecating the previous behavior.
D.3 Implicit declaration of copy functions [depr.impldec]
The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor. In a future revision of this International Standard, these implicit definitions could become deleted
This means that compilers keep generating a defaulted copy constructor, assignment operator and destructor if no user-defined declaration is found, but at least now they might issue a warning.
§ 12.8 / 7 copy ctor
If the class definition does not explicitly declare a copy constructor, one is declared implicitly.
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
§ 12.8 / 18 copy assignment
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly.
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.
- 3.1 对于 copy ctor: 为了兼容,
move ctor,只在
Rule of Five里的其他四个函数没有被自定义的时候自动生成。move assignment,只在
Rule of Five里的其他四个函数没有被自定义的时候自动生成。
言下之意:相比 C++11 以前的规则,加入了两个 move function,copy function 只是加了 move function 的限制。两个 move function 则完全遵守 Rule of Five 的规则。
参考
习惯了对网络上的中文资料保持警惕,却偏信了英文资料,细节方面还是得参考权威。Wikipedia, Microsoft Docs, fluent cpp 说法细节上的不一致甚至错误,耗费了太多时间。