条款5 了解C++默默编写并调用哪些函数
默认函数只在被需要的时候才产生
编译器产出的析构函数是non-virtual的
在内含const成员、reference成员的类中,编译器会拒绝编译赋值操作的代码。因为C++并不允许让reference和const对象改指向不同对象
class的析构函数自动调用 non-static 成员变量的析构函数
条款6 若不想使用编译器自动生成的函数,就该明确拒绝
侯捷给出的做法是将拷贝相关函数声明为private并且不予实现 过时
C++11可以通过在相关函数的声明后跟=delete
实现删除该函数
- 将拷贝函数声明为private并不绝对安全 因为成员函数和友元函数还是可以调用private函数
- 如果声明为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-cast
和typeid
也会把对象视作base class
一个可行的做法是 在derived class的构造函数中 将一个static派生类对象作为参数传递给base class的构造函数 这样就避免了调用一个未初始化/未生成的对象
条款10 令operator=返回一个reference to *this
内置数据类型可以连锁赋值 如果希望自定义数据类型也支持连锁赋值 那么必须令operator=返回一个this的引用
条款11 在operator=中处理“自我赋值”
不要认为自我赋值不存在
a[i]=a[j] //潜在的自我赋值
这一类并不明显的自我赋值 是“别名”带来的结果
一般来说 在operator=的最前面加上一个“证同测试”即可 但是这个方法不是异常安全的
正确操作:记住原来的 令其指向新的 再删除它
条款12 复制对象时勿忘其每一个成分
当为派生类写拷贝函数的时候必须要考虑到基类的成分 那些成分有可能还private的 所以必须小心复制基类成分,调用基类的拷贝函数
不应该让拷贝赋值操作符调用拷贝构造函数,因为这种操作类似于:试图构造一个已存在对象.
当发现拷贝构造函数和拷贝赋值操作符之间有相近的代码 消除重复代码的做法是 建立一个新的成员函数给两个函数调用 该函数一般是private的 且常被命名为init