本篇笔记包含 Term 05~12。
Term 05:了解 C++ 默默编写并调用了哪些函数
编译器会为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符以及析构函数,它们都是 public 以及 inline 的。注意到,当 copy assignment 操作符在父类中被声明为 private 或者含有 const 成员面对等情况,编译器不会自动创建 copy assignment 操作符;编译器自动创建的析构函数不是虚函数,除非继承自虚函数。
Term 06:若不想使用编译器自动生成的函数,就应该明确拒绝
通过 Term 05 我们知道编译器会自动生成一些成员函数,但有时我们可能不需要这种功能。解决方法:
- 将该成员函数声明为 private 并且不予实现
- 按照 1. 构建一个基类,然后去继承它
- 使用 C++(11) 提供的
=default
和=delete
显式启用或者禁用编译器自动版本的函数
Term 07:为多态基类声明 virtual 析构函数
- 用作多态的基类应该声明 virtual 析构函数。如果 class 带有 virtual 成员函数,应该声明 virtual 析构函数
- 不是基类或者不是用作多态的基类,不应该声明 virtual 析构函数,比如 STL 容器
- 以上两点可以避免内存泄漏
Term 08:别让异常逃离析构函数
不应该在析构函数内抛出异常,保证析构函数能执行。将可能造成异常的部分移到另一个函数内或者交给用户自己处理。在 C++(11) 中,可以使用 noexcept
来限制函数,如果出现异常会直接终止程序。
Term 09:绝不在构造和析构过程中调用 virtual 函数
如果在 base class 构造和析构过程中调用 virtual 函数,该函数的版本是 base class 对应的版本,不会是意图的 derived class 版本。
Term 10:令 operator= 返回一个 reference to *this
可以实现右结合的连锁赋值,大家都这么做,一种习惯性接口。
Term 11:在 operator= 中处理“自我赋值”
- 如果将一个对象赋值给自身,会造成安全问题。处理方法可以是:通过地址判断是否是同一对象;安排安全的语句顺序;copy-and-swap
- 任何函数,如果操作多个对象,要考虑其中某些对象其实是同一个这种情况
Term 12:复制对象时勿忘每一个成分
- copy 构造函数和 copy 赋值操作符会执行拷贝
- 拷贝对象时应该把每个成员变量都考虑到,并且调用对应的 base class 的函数
- copy 构造函数和 copy 赋值操作符之间不能互相调用,可以考虑新创建一个成员函数维护冗余代码,然后在 copy 操作时调用