本篇笔记包含 Term 05~12。

Term 05:了解 C++ 默默编写并调用了哪些函数

编译器会为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符以及析构函数,它们都是 public 以及 inline 的。注意到,当 copy assignment 操作符在父类中被声明为 private 或者含有 const 成员面对等情况,编译器不会自动创建 copy assignment 操作符;编译器自动创建的析构函数不是虚函数,除非继承自虚函数。

Term 06:若不想使用编译器自动生成的函数,就应该明确拒绝

通过 Term 05 我们知道编译器会自动生成一些成员函数,但有时我们可能不需要这种功能。解决方法:

  1. 将该成员函数声明为 private 并且不予实现
  2. 按照 1. 构建一个基类,然后去继承它
  3. 使用 C++(11) 提供的 =default=delete 显式启用或者禁用编译器自动版本的函数

Term 07:为多态基类声明 virtual 析构函数

  1. 用作多态的基类应该声明 virtual 析构函数。如果 class 带有 virtual 成员函数,应该声明 virtual 析构函数
  2. 不是基类或者不是用作多态的基类,不应该声明 virtual 析构函数,比如 STL 容器
  3. 以上两点可以避免内存泄漏

Term 08:别让异常逃离析构函数

不应该在析构函数内抛出异常,保证析构函数能执行。将可能造成异常的部分移到另一个函数内或者交给用户自己处理。在 C++(11) 中,可以使用 noexcept 来限制函数,如果出现异常会直接终止程序。

Term 09:绝不在构造和析构过程中调用 virtual 函数

如果在 base class 构造和析构过程中调用 virtual 函数,该函数的版本是 base class 对应的版本,不会是意图的 derived class 版本。

Term 10:令 operator= 返回一个 reference to *this

可以实现右结合的连锁赋值,大家都这么做,一种习惯性接口。

Term 11:在 operator= 中处理“自我赋值”

  1. 如果将一个对象赋值给自身,会造成安全问题。处理方法可以是:通过地址判断是否是同一对象;安排安全的语句顺序;copy-and-swap
  2. 任何函数,如果操作多个对象,要考虑其中某些对象其实是同一个这种情况

Term 12:复制对象时勿忘每一个成分

  1. copy 构造函数和 copy 赋值操作符会执行拷贝
  2. 拷贝对象时应该把每个成员变量都考虑到,并且调用对应的 base class 的函数
  3. copy 构造函数和 copy 赋值操作符之间不能互相调用,可以考虑新创建一个成员函数维护冗余代码,然后在 copy 操作时调用