条款5 了解C++默默编写并调用哪些函数

默认函数只在被需要的时候才产生

编译器产出的析构函数是non-virtual的

在内含const成员、reference成员的类中,编译器会拒绝编译赋值操作的代码。因为C++并不允许让reference和const对象改指向不同对象

class的析构函数自动调用 non-static 成员变量的析构函数

条款6 若不想使用编译器自动生成的函数,就该明确拒绝

侯捷给出的做法是将拷贝相关函数声明为private并且不予实现 过时

C++11可以通过在相关函数的声明后跟=delete实现删除该函数

  1. 将拷贝函数声明为private并不绝对安全 因为成员函数和友元函数还是可以调用private函数
  2. 如果声明为private且不定义的话 对它们的调用将会获得一个 链接错误 一般报错为“undefined reference to …”

所以其实还是不太方便

条款7 为多态基类声明virtual析构函数

派生类对象被绑定到基类指针或者引用上 在调用析构函数时,如果不是virtual析构函数,那么派生类中的部分没有被销毁

欲实现virtual函数 对象必须携带某些信息 主要用来在运行期决定哪一个virtual函数被调用 这些信息通常由vptr指针指出 vptr指向一个由函数指针构成的数组,称为vtbl(virtual table) 每一个带有virtual函数的class都有一个相应的vtbl 当对象调用某一virtual函数 实际上取决于该对象的vptr所指向的那个vtbl

如果类内包含virtual函数,其对象体积会增加

纯虚析构函数需要在类外提供一份定义!!

条款8 别让异常逃离析构函数

类不希望析构函数可以产生异常

析构函数应该捕捉任何异常 然后吞下它们或结束程序

如果需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数而非在析构函数中执行该操作

条款9 绝不在构造或析构函数中调用virtual函数

如果在构造函数中调用virtual函数 ,那么创建一个派生类对象的时候,由于基类构造优先于派生类构造,此时调用的virtual函数是基类的对应函数因为在基类构造期间virtual函数不是virtual函数

在派生类对象的基类构造期间,对象的类型是base class而不是derived class

不止virtual函数会被编译器解析为基类的函数,如果使用运行期类型信息 dynamic-casttypeid 也会把对象视作base class

一个可行的做法是 在derived class的构造函数中 将一个static派生类对象作为参数传递给base class的构造函数 这样就避免了调用一个未初始化/未生成的对象

条款10 令operator=返回一个reference to *this

内置数据类型可以连锁赋值 如果希望自定义数据类型也支持连锁赋值 那么必须令operator=返回一个this的引用

条款11 在operator=中处理“自我赋值”

不要认为自我赋值不存在

  1. a[i]=a[j] //潜在的自我赋值

这一类并不明显的自我赋值 是“别名”带来的结果

一般来说 在operator=的最前面加上一个“证同测试”即可 但是这个方法不是异常安全的

正确操作:记住原来的 令其指向新的 再删除它

条款12 复制对象时勿忘其每一个成分

当为派生类写拷贝函数的时候必须要考虑到基类的成分 那些成分有可能还private的 所以必须小心复制基类成分,调用基类的拷贝函数

不应该让拷贝赋值操作符调用拷贝构造函数,因为这种操作类似于:试图构造一个已存在对象.

当发现拷贝构造函数和拷贝赋值操作符之间有相近的代码 消除重复代码的做法是 建立一个新的成员函数给两个函数调用 该函数一般是private的 且常被命名为init