effective C++(二)

来源:互联网 发布:csdn知乎 编辑:程序博客网 时间:2024/05/17 09:41

接上篇文章,本问介绍构造、析构、赋值运算的注意事项...

       5:了解C++默默编写并且调用的函数。有一个空类的时候,如empty class,C++处理过它之后编译器就会自己声明一个拷贝构造函数、一个拷贝赋值操作符、一个默认构造函数和一个析构函数。如果有声明自己的构造函数,编译器就不会再自动声明。注意:如果自己生成的代码合法且有机会证明它有意义才会自动生成,否则编译器拒绝生成operator =()(C++不允许让引用改指向不同对象)。

       6:若不想使用编译器自动生成的函数,就该明确拒绝。如果一个类的对象时独一无二的,例如iostream库或者一个描述“房子”的对象,我们此时并不希望对这些对象进行拷贝,因此无论时拷贝构造函数还是拷贝赋值运算符都不希望生效,那么我们该如何解决?答案是:将函数声明为private,而且故意不实现它们,只是声明。此时编译器不会自动声明该类函数,并且一般用户无法使用它们,而member函数和friend函数使用它们的时候也会出现连接错误的提示。将连接期错误转移指编译期也是可能的,方法是专门生成一个uncopy类用于被其他类继承,uncopy类本身是个空类,只是私有的声明了拷贝构造函数和拷贝赋值函数,并且没有实现它们。

        7:为多态基类声明声明virtual析构函数。若一个基类被多个子类继承,采用工厂模式时,会返回一个基类指针,指向新生成的子类对象(如,A a = new(B),其中A是B的父类)。此时如果释放该指针,且基类析构函数不为virtual时,所释放的指针便会是基类部分,而子类部分的指针并没有释放,这可能造成资源泄露、败坏数据结构等恶果。因此任何class只要确定带有virtual函数,就确定要带有virtual析构函数。

       如果class不含virtual函数,通常表示它并不想被继承。例如:一个class的对象想要和其他语言进行互通,将此对象传递过去,但是如果class中添加了virtual函数,那么这些对象必须携带某些信息来决定运行期执行哪个函数。这通常是由vptr和vtbl实现的,而增加这些信息会使对象大小改变,因此不能再将其传递至其他语言撰写的函数,除非对vptr进行补偿。因此:只有当class内至少含有一个virtual函数,才将其析构函数声明为virtual。有时候给class带一个纯虚析构函数会更便利,纯虚函数会导致抽象类,它不能被实体化只能被继承。

       8:别让异常逃离析构函数。C++不喜欢析构函数抛出异常。假设在一个vector调用结束后需要析构vector中的10个对象时,销毁每个对象都可能抛出异常,那么当C++检测到多于两个异常时就会导致不确定行为。有两个方法可以避免这一问题:一是如果析构函数中有函数抛出异常,就在析构函数中调用abort()来终止程序;二是在析构函数中捕获异常,并且记录下错误信息,即吞下异常。这两种方法都是由析构函数自己解决异常,最好的办法是可以提供由客户使用的新函数来处理会抛出异常的函数,如果用户没有处理,再转由析构函数处理。这是一个双保险,用户可以自己来调用该“危险函数”并在非析构函数中处理异常,也可以不调用“危险函数”在析构函数中处理。

       注意两点:析构函数绝对不要抛出异常;如果客户想要对某个操作运行期间抛出的异常做反应,class应该提供一个普通函数而非析构函数执行该操作。

       9:决不在构造和析构过程中调用virtual函数。假设一个基类中的构造函数调用了基类中的virtual fun()函数,其子类中也有相应的fun()实现方法。其子类初始化一个子类对象时首先调用父类构造,我们的本意是子类初始化对象时会先调用父类的virtual fun()继而转到子类的fun()中,但是事实上不会实现以上 转移,在构造期间虚函数并不是真正的虚函数。因为虚函数通常会使用子类的成员变量,但是在基类构造时子类的成员变量还没有初始化,此时使用的话会造成变量未定义行为。对象在derived class 构造函数开始执行之前不会成为一个derived class对象。

       另外一种潜在错误是在构造函数中调用成员函数,而该成员函数中调用了virtual函数,这也会造成以上问题。一种很好的解决方法是:在基类中将虚函数改为非虚函数,然后要求子类传递相应的信息给基类。我们无法使用virtual函数从base class向下调用,在构造期间可以领derived class将必要信息上传至base class。

        10:令operator= 返回一个reference to *this。赋值可以写成连锁形式:x=y=z=15即x=(y=(z=15))。为了实现以上连锁赋值,赋值操作符必须返回一个引用,该引用操作符的左侧实参。另外+=、-=等赋值操作符也有以上协议。

0 0
原创粉丝点击