Effective C++学习第三天

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

       当我们创建一个base class指针指向新生成的derived class时,当删除基类指针时,如果base class是一个non-virtual析构函数时,实际执行的结果通常是derived class中的base成分被销毁,derived成分没有被销毁;

      通常解决这种问题的方法是把base class中的析构函数声明为virtual;

      并不是只有析构函数可以声明为virtual,其他的函数也可以,virtual函数的目的是为了让derived class客制化,也就是实现动态联编;

     任何class中只要带有virtual函数都需要提供一个virtual析构函数;

    想要实现虚函数,对象必须提供某些信息,来决定运行期决定那个virtual函数被调用,通常这个信息有vptr(virtual table pointer)来表示,vptr指向一个函数指针构成的数组,称为vtbl(virtual table),每一个带有virtual函数的class都有一个对应的vtbl,对象调用virtual函数的时候,实际上调用的是vptr指向的vtbl;

     一般来说,如果不想把base class用于继承,一般不要定义virtual函数,毕竟vptr指针会占内存的;不要试图去继承一个不带虚析构函数的类,这样会出问题的;

     抽象类(不能被实体化的类,不能用这种类来创建对象):类中带一个pure virtual析构函数,通常作为base classes;由于析构的顺序和初始化的顺序相反,因此在声明纯虚析构函数时,需要给虚析构函数提供一份定义,这样编译器在derived class的析构函数创建一个对base class析构函数的调用动作,即使在base class中未发生析构,代码如下:

 class AMOV{

public:

         virtual ~AMOV() =0;//pure virtual析构函数

};

AMOV::~AMOV() { }

    结论:polymorphic(带多态性质的)base classes应该声明一个virtual析构函数,如果class带有任何的virtual函数,那么应该有一个virtual析构函数;

             classes的设计目的如果不是作为base classes使用,或不是为了多态性就不应该声明virtual析构函数;

2:别让异常逃离析构函数

        在两个异常同时存在的情况下,程序若不结束执行就会导致不明确的行为;

class dbconnection{                                                         class dbconn{//管理对吧connection对象

public:                                                                               public:

          static dbconnction create();//创建对象                           ~dbconn()  {      db.close() ;   }//关闭数据库连接

          void close();//关闭系统                                               private:  dbconnection db;

};                                                                                       };

 问题:如果close()调用失败,就会产生异常,而析构函数中没有处理异常的机制,异常在析构函数向上抛出;

解决方案:在析构函数中建立异常处理机制或者直接吞掉异常(如果在吞掉异常后,程序依旧可以可靠的运行);                    ~dbcnn(){

               try {db.close();}

               catch(...){//捕获异常

                           std::abort() ;//终止程序              //方案1需要这一句        //方案2不需要

                          }

}

另外一种比较好的方法就是:重新设计dbconn接口,设置一个普通函数让用户判断是否会产生异常,

class dbcnn{

public:

 void close(){

             db.close();

             closed=true;

             } 

~dbcnn() {

if(!closed){

        try{

                db.close();

         }

         catch(...){

        }

    }

  }

private:

            dbconnection db;

            bool closed;

};

3:不要在构造和析构函数中调用virtual函数

          对于构造函数而言,不能在构造过程中调用virtual函数的原因:base class在构造过程中,virtual函数不会下降到derived class,因为在base class构造函数执行时derived class的成员还没有初始化,如果此时virtual函数下降到derived class中,势必会调用未初始化的变量,而C++不允许这一现象发生,在base class构造期间,virtual函数不再是virtual函数;更本质的原因是在derived class对象的base class构造期间,对象是base class而不是derived class,不只是virtual函数会被编译器解析为base class,若使用运行期类型信息(如dynamic_cast和typeid),也会把对象视为base class类型,因此对象在derived class构造函数开始执行前不会成为一个derived class对象;

         对于析构函数也是同样的道理(构造和析构的顺序相反):一旦derived class析构函数开始执行,对象内的derived class成员变量便呈现出未定义状态(因为被析构了),进入base class析构函数后对象变成一个base class对象;

        为了防止传值过程中出现意外指向未初始化的成员变量,可以充分利用static关键字;

4:令operator=返回一个reference to*this

      为了使操作符实现连锁操作,操作符必须返回一个reference指向操作符的左侧实参;


     

猜你喜欢

转载自blog.csdn.net/xx18030637774/article/details/80771306