为多态基类声明一个虚析构函数(Effective C++_7)

来源:互联网 发布:淘宝极速退款五千额度 编辑:程序博客网 时间:2024/05/16 06:17

一、声明虚析构函数的原因

(1)先考虑没有声明虚析构函数的代码:

#include <iostream>using namespace std;class Base{public:    Base(int x,int y):a(x),b(y){        cout<<"Base Construction"<<endl;    }    virtual void display()=0;     ~Base(){        cout<<"Base Destuction"<<endl;    }private:    int a;    int b;};class D1:public Base{public:    D1(int x,int y,int z):Base(x,y),c(z){        cout<<"D1 Construction"<<endl;    }     void display(){        cout<<"D1 Class"<<endl;    }     ~D1(){         cout<<"D1 Destuction"<<endl;     }private:    int c;};class D2:public D1{public:    D2(int x,int y,int z,int w):D1(x,y,z),d(w){        cout<<"D2 Construction"<<endl;    }    ~D2(){        cout<<"D2 Destuction"<<endl;    }    virtual void display(){        cout<<"D2 Class"<<endl;    }private:    int d;};int main(){     Base* p=new D1(1,2,3);    p->display();    delete p;    return 0;}

其结果是:

Base ConstructionD1 ConstructionD1 ClassBase Destuction请按任意键继续. . .

这里没有把析构函数声明为虚析构函数,那么,销毁对象时,只调用了基类的析构函数;假如,把基类的析构函数声明为虚析构函数,那么就会有下面的结果:

Base ConstructionD1 ConstructionD1 ClassD1 DestuctionBase Destuction请按任意键继续. . 

当派生类的对象从内存中撤销时一般先调用派生类的析构函数,然后再调用基类的析构函数。但是,如果用new运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量。在程序用带指针参数的delete运算符撤销对象时,会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数;当声明为虚析构函数后,则会调用所有的析构函数,不会造成内存泄漏


二、何时声明为虚析构函数
(1)并不是所有c++类都应该将析构函数设置为virtual。只有具有virtual函数的多态基类(或者其它想当base class的类)才应该将析构函数设置为virtual,对于普通的类则无必要

因为虚函数的实现要求对象携带额外信息,也就是维护一个指向虚函数表的指针vfptr(virtual table pointer),vfptr指向虚函数表vtbl(virtual table)。当有虚函数时,声明一个对象时,对象的首地址会存储vfptr指针,而无虚函数时,则没有这样的指针;那么这会给对象带来多余的负担,所以对于非多态基类,没必要将析构函数声明为virtual以带来额外负担。

(2)纯虚析构函数:如果某个class只希望作为base class(不希望被实例化),但是又没有一个纯虚函数,而base class应该有一个virtual析构函数,那么此时就可以将析构函数设置为纯虚函数。必须为纯虚析构函数提供定义,否则会出现Link错误;

class Base{virtual ~Base()=0;}Base::~Base(){};

(3)如果一个类的不是设计作为基类来使用,或不是为了具备多态性,就不应该声明virtual析构函数


三、基类并非均是为了多态

(1)标准库中的STL容器和string类,不是设计作为基类使用的,它们均没有虚析构函数,不要继承它们
(2)比如Uncopyable的设计是为了明确拒绝赋值,而不是为了设计成多态,所以也不需要虚析构函数


参考:Effective C++ 3rd(侯捷译)

0 0