您的位置:首页 > 编程语言 > C语言/C++

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

2015-09-07 18:00 232 查看
一、声明虚析构函数的原因

(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 Construction
D1 Construction
D1 Class
Base Destuction
请按任意键继续. . .


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

Base Construction
D1 Construction
D1 Class
D1 Destuction
Base 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(侯捷译)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: