第二章 构造/析构/赋值运算
1、编译器可以暗自为class创建default构造函数、copy构造函数、copy assiginment操作符,以及析构函数。
2、为驳回编译器自动提供的机能,例如copy构造函数和copy assiginment操作符,可将相应的成员函数声明为private并且不予实现。
3、当derived class对象经过由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没有被销毁。消除这个问题的做法是:给base class一个virtual析构函数。
任何class只要带有virtual函数都几乎确认应该也有一个virtual析构函数。
当class不企图被当作base class,令其析构函数为virtual往往是个馊主意。因为如果class内含有virtual函数,其对象的体积会增加。
总结: 带多态性质的base classes应该声明一个virtual析构函数。当不做base classes使用,或不是为了具备多态性,就不应该声明virtual析构函数。
4、当析构函数吐出异常,程序可能过早结束或出现不明确行为。当析构函数必须执行一个动作,而这个动作可能会抛出异常,当析构函数抛出异常,也就是允许它离开这个析构函数,那会造成问题,因为那就是抛出了难以驾驭的麻烦。
解决办法:通过调用abort完成。调用abort可以抢先制“不明确行为”于死地。但将异常吞掉,压制了“某些动作失败”的重要信息。另一个办法,class应该提供一个普通函数执行该操作。
总结:
- 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们或结束程序。
- 如果客户端对某个操作函数运行期间抛出异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。
5、不要在构造函数和析构函数中调用virtual函数,因为vptr指针是分步完成的,在基类的构造函数中调用virtual函数,并不会发生多态,而是直接调用了基类的virtual函数版本。
6、在重载operator=运算符时,需返回一个引用,原因是为了形成连锁等于的形式,因为返回引用可以当左值。
7、在operator=中处理“自我赋值”,在赋值操作时,我们都会先删除旧对象,在根据新的对象值创建一个新对象返回,但如果新对象和旧对象一样,即指向同一个空间,则在最初删除旧对象时,已经把新对象的值已删除,所以会出现问题,应该在最初判断下两者是否相等,当相等时直接返回自身。
8、复制对象时勿忘其每一个成分。当存在继承时:(1)复制所有local成员变量,(2)调用所有base classes内适当的copying函数。当copy构造函数和copy assignment操作符有相近代码时,不要尝试以某个copying函数实现另一个copying函数,而是建立一个新的成员函数给两者调用,这样的函数往往是private而且常被命名为init。