条例6~10(构造/析构/赋值函数)

条例六

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

  • 当你遇到想要禁止掉拷贝操作的时候,即使你自己不写拷贝,编译器也会生成一份,这时可以只声明拷贝操作并将其放在private内。private内可以防止类外成员调用。(这时遇到的是编译)不声明定义可以防止友元或者成员函数调用。这样当试图调用的时候就会遇到链接错误。
  • 我们可以将链接期的错误移到编译期。我们需要为拷贝函数单独创建一个类,并将拷贝函数放在这个类的private里,同时当前类不再声明拷贝,让当前类继承这个防拷贝的类。这样当任何人试图拷贝当前类的时候,编译器会试图自动生成拷贝函数,并试图调用父类的拷贝函数,并用于父类的拷贝函数是私有调不动,就会出现编译错误。

总结

  • 若不想使用编译器自动生成的成员函数,可以通过只声明不实现,或者将拷贝函数作为父类并私有化

条例七

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

  • 由于父类指针可以指向父类对象或者子类对象,若析构函数没有实现多态,这时程序结束是调用的析构函数永远是父类的析构函数。这导致若当前指向的对象是子类对象就导致子类对象本身的成员没有被销毁。造成局部销毁。实现多态后就可以自动调用析构函数。
  • 若当前类不想被继承,则析构函数最好不然要声明为虚继承。因为引入虚函数就意味引入虚表,就会增加一个虚表指针,在某些情况下会增加类总体的大小,同时因为有的语言不支持虚表,可移植性就会变差
  • 只有当class内至少包含一个虚函数的时候再将析构函数声明为析构函数。
  • 不要试图继承所有stl容器,不然在析构的时候可能会造成资源泄漏
  • 若希望拥有一个抽象类但不想提供任何纯虚函数,可以将析构函数声明为纯虚函数,但这是需要在类外给当前析构函数提供定义,因为子类继承当前类后,在析构的时候再调用子类的析构函数后就会调用父类的析构函数,若这时没提供父类的析构定义就会有链接错误
  • 不需要实现多态的类不要提供虚析构函数

总结

  • 带有多态性质的父类应该声明虚析构函数,若类内有任意一个虚函数,则析构函数也应该为虚函数
  • 若类的设计不是作为父类使用,或不是为了多态,则不需要虚析构函数

条例八

别让异常逃离析构函数

  • 由于析构函数会被程序自动调用,若有多个变量要析构,在析构第一个变量的时候抛出异常,其他变量也应该被析构,这导致多个析构抛出异常就会导致行为不明确。
  • 可以创建一个管理当前资源的类,让当前管理类的析构函数里写上资源类里析构里想执行的操作。(设为close)有种托管的感觉。
  • 若此时close抛出异常我们有三种处理方法,直接调用abort结束程序,这样能阻止异常传播出去。或者吞下异常,这会导致压制住了某些重要信息但能让程序可靠的继续运行。第三种方法较好些,在管理类设计一个新的接口,调用资源类的close函数,同时创建标记位记录资源类是否被close,并在析构函数内判断是否close,若没有则可以再次调用。若出现异常可以结束程序或者忽略。这样就是双重保险,让close的责任从析构函数转移到用户上,这样就给了用户捕捉异常的机会,若用户不捕捉,就会在析构函数内处理异常,也就是结束程序或者吞下。我们给用户提供处理异常的机会,是用户自己选择忽略。

总结

  • 析构函数不能吐出异常,只能吞下或者结束进程
  • 若客户要对某个操作函数进行异常捕捉,应该在不同函数内执行。

条例九

绝不再构造函数和析构过程中调用virtual

  • 不应该在构造函数和析构函数中调用虚函数,否则会导致预料之外的结果。假设某一操作实现多态,并在父类和子类的构造函数中调用这一函数,当子类构造的时候,由于他会调用父类的构造函数,这时就会调用父类构造函数的对应操作而不是子类构造对应操作。也就是说父类构造函数的虚函数不会下降到子类(此时虚函数不是虚函数)
  • 跟合理的解释,父类构造函数执行构造更早,当父类构造函数被调用时,子类还没有被构造,由于不能调用未被初始化的成员,此时就会调用父类构造内对应的函数。也就是调用对象其实是父类。
  • 所以析构函数和构造函数内执行的操作最好改为非虚函数,通过传值辨别调用对象。

总结

  • 在构造函数和析构函数内不要调用虚函数,因为这种调用不会下降到子类对象

条例十

让operator= 返一个this*的引用

  • 这样能实现连锁赋值,并和stl保持一致

总结

  • 令赋值操作符返回一个this*的引用

你可能感兴趣的:(Effective,c++,c++)