C++中为什么有时基类的析构函数必须写为虚函数

来源:互联网 发布:网络说鸡汤什么意思 编辑:程序博客网 时间:2024/06/08 04:34

C++中为什么有时基类的析构函数必须写为虚函数

    说到虚函数之前,先简单解释一下多态,我的这篇博文:http://blog.csdn.net/zcyzsy/article/details/52463355  里有很详细的解释多态,这里简单说下:

不同对象调用相同名称的成员函数时,可能引起不同的行为(即执行不同的代码)。这种现象称为多态性。将函数调用连接相应函数体的代码的过程称为函数联编(简称联编)。C++中根据联编时刻不同,分为静态联编和动态联编。

 

静态联遍:不同的类可以有相同名称的成员函数,编译器在编译时对他们进行函数联编,这种在编译时刻进行的联编称为静态联编。静态联编所支持的多态性就是编译时多态性(也称编译期多态性、静态多态性)。函数重载和运算符重载就属于编译时多态性。

 

动态联编:在动态联编中,在程序运行时才能确定调用哪个函数。这种在运行时的函数联编称为动态联编。动态联编所支持的多态性就是运行时多态性(也称运行期多态性、动态多态性)C++中,只有虚函数才可能是动态联编的。可以通过定义类的虚函数和创建派生类,然后在派生类中重新实现虚函数,实现具有运行时的多态性。

   综上,知道虚函数的作用了,那么为什么析构函数有时要声明为虚函数?

编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。所以通常是将析构函数声明为虚函数。一个基类的函数一旦声明为虚函数,那么不管你是否加上virtual修饰符,它在所有派生类中都成为虚函数。但是由于理解明确起见最好还是加上virtual修饰符。      

     

简言之:在实现多态时,当用基类指针操作派生类对象时,在析构时防止只析构基类而不析构派生类的状况发生,因此将基类的析构函数声明为虚函数。

 

测试代码如下:

1

#include<iostream>using namespace std;class BaseClass{public:    BaseClass() {};    ~BaseClass() { cout << "this isdestructor of BaseClass!" << endl; };    void fun() { cout << "this is fun of BaseClass" << endl; };}; class DerivedClass:public  BaseClass{public:    DerivedClass() {};    ~DerivedClass() { cout << "this isdestructor of DerivedClass!" << endl; };    void fun() { cout << "this is fun of DerivedClass" << endl; };}; int main(){    DerivedClass *p = new DerivedClass;    p->fun();    delete p;    system("pause");    return 0;}


 

运行结果:

this is fun ofDerivedClass

this isdestructor of DerivedClass!

this isdestructor of BaseClass!

 

这段代码中基类的析构函数不是虚函数,main函数中用派生类的指针去调用派生类的函数,释放指针P的过程是:先释派生类的资源,再释放基类资源

 而换成BaseClass* p=new BaseClass;

运行结果:

this is fun of BaseClass

this isdestructor of BaseClass!

 

2.

#include<iostream>using namespace std;class BaseClass{public:    BaseClass() {};    ~BaseClass() { cout << "this isdestructor of BaseClass!" << endl; };    void fun() { cout << "this is fun of BaseClass" << endl; };}; class DerivedClass:public  BaseClass{public:    DerivedClass() {};    ~DerivedClass() { cout << "this isdestructor of DerivedClass!" << endl; };    void fun() { cout << "this is fun of DerivedClass" << endl; };}; int main(){    BaseClass *p = new DerivedClass;    p->fun();    delete p;    system("pause");    return 0;} 


输出结果:

this is fun ofBaseClass

this isdestructor of BaseClass!    

这段代码中基类的析构函数同样不是虚函数,不同的是在main函数中用基类的指针指向派生类对象,释放指针P的过程是:只是释放了基类的资源,而没有调用派生类的析构函数.调用fun()函数执行的也是基类定义的函数(没有实现多态).这样的delete只能够删除基类对象,而不能删除子类对象,会造成内存泄漏。

    在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员.如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数。析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。

 

3.

#include<iostream>using namespace std;class BaseClass{public:    BaseClass() {};    virtual ~BaseClass() { cout << "this isdestructor of BaseClass!" << endl; };    virtual void fun() { cout << "this is fun of BaseClass" << endl; };}; class DerivedClass:public  BaseClass{public:    DerivedClass() {};    ~DerivedClass() { cout << "this isdestructor of DerivedClass!" << endl; };    void fun() { cout << "this is fun of DerivedClass" << endl; };}; int main(){    BaseClass *p = new DerivedClass;    p->fun();    delete p;    system("pause");    return 0;}

  

运行结果:

this is fun of DerivedClass

this is destructorof DerivedClass!

this isdestructor of BaseClass!   

 这段代码中基类的析构函数被定义为虚函数,main函数中用基类的指针去操作派生类的成员,释放指针P的过程是:先释放了派生类的资源,再调用基类的析构函数.调用fun()函数执行的也是继承类定义的函数.  

 

4.

#include<iostream>using namespace std;class BaseClass{public:    BaseClass() {};    virtual ~BaseClass() { cout << "this isdestructor of BaseClass!" << endl; };    virtual void fun() { cout << "this is fun of BaseClass" << endl; };}; class DerivedClass:public  BaseClass{public:    DerivedClass() {};    ~DerivedClass() { cout << "this isdestructor of DerivedClass!" << endl; };    void fun() { cout << "this is fun of DerivedClass" << endl; };}; int main(){    DerivedClass *p = new DerivedClass;    p->fun();    delete p;    system("pause");    return 0;}


运行结果:

this is fun ofDerivedClass

this isdestructor of DerivedClass!

this isdestructor of BaseClass!

如上代码,fun() 不是虚函数,析构函数也不是虚函数,结果依然如此。

 

如上,一直用的是有时,而不是一定声明为虚函数,为什么呢?可不可以把析构函数默认为虚函数?

答:

如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间.所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数.

   C++不把虚析构函数直接作为默认值的原因是虚函数表的开销以及和C语言的类型的兼容性。有虚函数的对象总是在开始的位置包含一个隐含的虚函数表指针成员。

 

 


阅读全文
1 0