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

如果类没有自己定义,C++编译器会自动声明一个Copy构造函数、一个Copy赋值操作符、一个析构函数和一个default构造函数。所有这些函数都是public,inline的。但是这些类并不是总是会被创建出来,只有程序中有调用,才会被编译器产生出来。编译器生成的Copy构造函数、一个Copy赋值操作符只是只是简单的将来源对象的每一个non-static成员变量拷贝到目标对象(分别调用相应变量的类型的拷贝构造函数)。这时Copy构造函数、一个Copy赋值操作符是很相似的,但是还是有些不同。当如下两个条件任何一个符合时,编译器拒绝生成operator=:内含reference成员的;内含const成员的。因为C++的reference不能改指不同对象,const成员也不能修改,而在operator=中会试图对reference和const成员赋值,这是不应该发生的,所以编译器会拒绝生成。

 

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

举例来说,如果一个类中有个ID用于独一无二地区分对象,那在Copy构造或者Copy赋值时,就会使得两个对象的ID相同了,这不是你希望的。那就应该阻止程序调用Copy构造函数或者Copy赋值操作符,一个办法就是将这些函数声明为private,如果有实现的话,还难以避免friend和member函数调用它们,所以应该把不希望程序使用的函数声明为private,并且不实现!当客户试图拷贝其对象,编译器就会报错;当friend或者member函数调用它们,链接器会报错。下面的办法可以把第二种情况的链接器错误转化为编译器错误:新建一个新的base class,将其Copy构造函数、一个Copy赋值操作符声明为private,并让不希望被拷贝的类private继承这个base class,子类不再声明这些函数。这样做的原理就是:子类没有声明Copy构造函数、一个Copy赋值操作符,那么编译器会自动生成一个,但这些编译器生成的方法会调用基类的相应函数,而它们是private的(即使子类继承了基类,但任然不能访问基类的private),这时编译器会拒绝这样做。

 

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

C++规范指出:当子类对象经由一个基类指针被删除,而该基类带着一个non-virtual析构函数,其结果没有定义,实际执行时通常发生的是对象的子类部分没有被销毁,这样很容易造成资源泄漏、破坏的数据结构。通常好的做法是:任何class,只要带有virtual函数,也就应该有个virtual析构函数。从另外一个角度看,如果一个class没有virtual函数,也就意味着这个类不准备作为基类使用,这时加一个virtual 析构函数并不好。

如果有个类,你想让它成为抽象类,但是又没有合适的pure virtual函数,就可以考虑使用pure virtual 析构函数。但这时你必须为这个pure virtual析构函数提供一份定义,因为编译器会在子类析构函数中产生对基类析构函数的调用,如果这时基类的析构函数没有定义,就会造成链接器错误。

总之,只有用于多态用途的类,才应该使用virtual 析构函数,如果类的设计目的不是用于作为基类使用,不应该定义virtual析构函数,如果如noncopyable类似的也不用于“经由基类接口处置子类对象”,也不需要virtual 析构函数

 

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

如果在vector中存储了10个对象 ,而当试图销毁这个vector时,也会调用其中对象的析构函数,而若有两个对象在析构时抛出异常,就会造成不确定的行为。两个处理办法:在析构函数中catch这个异常,并abort程序;在析构函数中catch这个异常,并写log,但不abort。一般而言,析构函数吞掉异常不是好办法,因为它掩盖了某些潜在的问题。更好的办法是重新设计接口,使客户有机会对可能出现的问题做出反应,比如提供close函数,让客户调用这个函数,并处理可能发生的异常,在析构函数中根据该close函数是否被调用过,来决定是否要调用close函数。虽然客户如果不调用close函数,又会回到析构函数吞掉异常的情况,但那是客户不按规则写程序,即使出了问题,也是没有办法的事情了。

 

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

基类构造期间,virtual函数不会下降到子类中去,因为在基类构造时,对象的类型是基类,而不是子类。