虚析构函数问题:为什么要将基类的的析构函数设成虚的?

转自:http://blog.csdn.net/pathuang68/article/details/4156308

CSDN 网友问:
class  A
{
public :
   
~ A() 
   { 
      cout 
<   < " A::~A "   <   < endl; 
   }
};


class  B: public  A
{
public
   
virtual   ~ B() 
   { 
      cout 
<   < " B::~B "   <   < endl; 
   }
};


class  C: public  B
{
public
   
~ C() 
   { 
      cout 
<   < " C::~C "   <   < endl; 
   }
};

 
int  main()

   A 
* a = new  A(); 
   B 
* b = new  B(); 
   C 
* c = new  C(); 
   A 
* d = new  B(); 
   A 
* e = new  C(); 
   B 
* f = new  C(); 

   delete a; 
   cout 
<   < endl; 
   delete b; 
   cout 
<   < endl; 
   delete c; 
   cout 
<   < endl; 
   delete d; 
   cout 
<   < endl; 
   delete e; 
   cout 
<   < endl; 
   delete f; 
   cout 
<   < endl; 

   system(
" Pause " );
}

这段程序运行时有错,当时考的时候题目直接说写出运行结果,我就稀里糊涂得写下来,回来编下发现有错,请教下错在哪,最好告诉我为什么?

  

玄机逸士回答:

1. 一般来说,如果一个类要被另外一个类继承,而且用其指针指向其子类对象时,如题目中的A* d = new B();(假定A是基类,B是从A继承而来的派生类),那么其(A)析构函数必须是虚的,否则在delete d时,B类的析构函数将不会被调用,因而会产生内存泄漏和异常;

2. 在构造一个类的对象时,先构造其基类子对象,即调用其基类的构造函数,然后调用本类的构造函数;销毁对象时,先调用本类的析构函数,然后再调用其基类的构造函数;

3. 题目给出的代码是可以编译的,但会出现运行时错误。错误出现在delete d;这一句。为讨论方便,我们不妨将A类和B类改写如下:


class  A
{
public :
    
int  a;
    
~ A()
    {
        cout 
<<   " A::~A "   <<  endl;
    }
};

class  B :  public  A
{
public :
    
int  b;
    
virtual   ~ B()
    {
        cout 
<<   " B::~B "   <<  endl;
    }
};


那么A* d = new B();这一句的左边所产生B的对象的内存结构如下:



A对象的内存结构如下:


可见 d 只能找到 a A 类的析构函数,而无法找到 B 对象的析构函数,所以当 delete d; 的时候, B 对象所占用的内存就此被泄漏掉了,也从而产生了异常。

如果将
A 类的析构函数设为虚的,那么 A 类对象的内存结构将为:


B类对象的内存结构为:


此时通过A* d = new B();A对象的内存结构中的vfptr,即虚函数表指针,就是B对象的vfptr(B对象的vfptrbitwise copy,即浅拷贝到A对象的vfptr。如B是从A虚继承而来的,则需要加一个offset,情况要更复杂,见http://blog.csdn.net/pathuang68/archive/2009/04/24/4105902.aspx),因此,A对象的vfptr所指向的是B对象的虚函数表,而B的析构函数位于书函数表0的位置,因此,这样就可以通过A类对象的指针d,找到B类对象的析构函数,从而在delete d;时,可以销毁B对象,而不会产生内存泄漏和异常。


事实上,该题目只要将A中的析构函数设成虚的,B类中的析构函数前面的virtual关键字不管是否存在,其析构函数也一定是虚的,C类同此道理。因此,得到结论就是,只要能够保证继承关系中最高的基类的析构函数是虚的,那么就不会产生前面所谈及的问题。这就是为什么在想使用多态特性的时候,需要将基类的析构函数设成虚的真正原因。

 

你可能感兴趣的:(虚析构函数问题:为什么要将基类的的析构函数设成虚的?)