[转] Effective c++学习笔记——条款07:为多态基类声明virtual析构函数

 from 
http://blog.csdn.net/wallwind/article/details/6762174

[置顶] Effective c++学习笔记——条款07:为多态基类声明virtual析构函数

分类: C/C++学习 3420人阅读 评论(25) 收藏 举报
Declare destructors virtual in polymorphic base classes
       1、为什么要申明虚函数
       C++程序设计中通常会存在一个基类有多个派生类型,而基类中的存在的大都是纯虚函数,需要派生类型实现。而这样的情况下,通过使用factory模式返回一个基类型的指针。在C++中明确指出,一个派生类型经过由一个基类型指针被删除,而该基类型带着一个non-virtual析构函数其结果未定义。只会造成一个局部的销毁,即基类型资源被释放,而派生类型造成memory leak,看下面的代码:
// Virtual_Const.cpp : 定义控制台应用程序的入口点。  
//  
  
#include "stdafx.h"  
#include <iostream>  
using namespace std;  
  
class Base{  
public:  
    Base(){}  
    ~Base(){ cout<<"~Base()"<<endl;}////不带virtual时输出的结果请看图一  
    //virtual ~Base(){ cout<<"~Base()"<<endl;}////正确的做法带virtual时输出的结果请看图二  
};  
    class Derived:public Base{  
    public:  
        Derived()  
        {  
            p=(char*)malloc(sizeof(char)*10);  
        }  
        ~Derived()  
        {  
            free(p);  
            cout<<"~Derived()"<<endl;  
        }  
    private:  
        char *p;  
    };  
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        Base *ptr=new Derived();  
        if (ptr==NULL)  
        {  
            cout<<"未分配成功"<<endl;  
            exit(1);  
        }  
        delete ptr;  
        return 0;  
    }  

 


图一:不带virtual时输出结果:
图二:带virtual时输出结果:
 
以上代码和输出结果可以看出,Derived类继承了Base类,并且在heap上申请了内存资源,在它的析构函数中被释放。但由于Base的析构函数是non-virtual,所以根本没有正确释放Derived类型中的ptr指针。
 
2、virtual 函数实现内部机制
       
         polymorphic(带多态性质的)base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不应该声明virtual析构函数。那么是不是所有的class都加上virtual析构函数以保万一,事实不是如此。如果没有必要,请不要为析构函数加上virtual二字。原因是virtual是有代价的,为了实现virtual函数,类中间必须要增加一个pointer指向虚函数表,这样增大了类的体积。所以没有必要的话,还是不要随意声明virtual的析构函数。普遍的规则是只有当类当中有virtual的函数时,析构函数才声明为virtual。也就是说这个基类是有多态性质(polymorphic)的。
        虚函数的实现需要它所在的对象包含额外的信息,这一信息用来在运行时确定本对象需要调用哪个虚函数。通常,这一信息采取一个指针的形式,这个指针被称为“ vptr ”(“虚函数表指针”)。 vptr 指向一个包含函数指针的数组,这一数组称为“ vtbl ”(“虚函数表”),每个包含虚函数的类都有一个与之相关的 vtbl 。当一个虚函数被一个对象调用时,就用到了该对象的 vptr 所指向的 vtbl ,在 vtbl 中查找一个合适的函数指针,然后调用相应的实函数。
请看如下代码,就知道声明virtual函数是要浪费系统资源的。
 
 
  1. // sev_vir.cpp : 定义控制台应用程序的入口点。  
    //  
    #include "stdafx.h"  
    #include <iostream>  
    using namespace std;  
    class class1{};  
    class class2  
    {  
    public:  
    virtual ~ class2();  
    };  
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        cout<<sizeof(class1)<<endl;  
        cout<<sizeof(class2)<<endl;  
        return 0;  
    }  

     


3、请注意

       不过还有一个问题,那就是继承那些不带virtual函数的标准类库的类,比如string类。如果你继承了string类,那么当你使用string的指针释放你的继承类的话,依然存在上面描述的问题,你的继承类没有释放它该释放的空间。因为string没有virtual的析构函数。所以请不要去继承这些并没有virtual析构函数的类。

        还有就是STL中的容器通常继承都毫无意义,因为它们也都没有提供virtual析构函数。C++并没有提供像JAVA或者C#的final和sealed这样的约束关键字来禁止某个类型被继承。 并不是每个类型都会被用来做继承用途,也不是每个类型设计出来被用来做为多态用途,所以正确的设计相当重要。

4、纯虚函数实现


  1. // Pure_vir.cpp : 定义控制台应用程序的入口点。  
    //  
    #include "stdafx.h"  
    #include <iostream>  
    using namespace std;  
    class Base //abstract class  
    {  
    public:  
        virtual ~Base(){};//virtual, not pure  
        virtual void func() const = 0;//pure virtual  
          
    };  
    void Base::func() const //pure virtual also can have function body  
    {  
        cout <<"Base::func"<<endl;  
    }  
    class Derived : public Base  
    {  
    public:  
        Derived(){}  
        virtual void func() const  
        {  
            Base::func();  
            cout <<"Derived::func"<<endl;  
        }  
        virtual void foo(){}  
    };  
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        Base* pb=new Derived();  
        pb->func();  
        pb->Base::func();  
        return 0;  
    }   

     


  从生成的结果我们可以看到有纯虚函数的类是抽象类,不能生成对象,只能派生。

        如果他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。定义纯虚函数就是为了让基类不可实例化化, 因为实例化这样的抽象数据结构本身并没有意义。或者给出实现也没有意义实际上我个人认为纯虚函数的引入 ,为了安全.因为避免任何需要明确但是因为不小心而导致的未知的结果,提醒子类去做应做的实现.。

请记住

1、polymorphic(带多态性质的) base classes应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。

2、Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性质(polymorphically),就不该声明virtual析构函数。

posted on 2012-08-30 11:46  Orz..  阅读(141)  评论(0)    收藏  举报

导航