`
helpbs
  • 浏览: 1163411 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

某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对象的vfptr被bitwise 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类同此道理。因此,得到结论就是,只要能够保证继承关系中最高的基类的析构函数是虚的,那么就不会产生前面所谈及的问题。这就是为什么在想使用多态特性的时候,需要将基类的析构函数设成虚的真正原因。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/pathuang68/archive/2009/05/07/4156308.aspx

分享到:
评论

相关推荐

    虚基类 虚函数成员 虚析构函数

    该资源的内容主要是 虚基类 虚函数成员 虚析构函数的具体的区别

    C++中基类的析构函数为什么要用virtual虚析构函数.pdf

    C++中基类的析构函数为什么要用virtual虚析构函数.pdf

    C++中虚析构函数的作用

    我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明:

    C++虚基类 虚函数 虚析构函数

    //析构函数做成员函数 }; Base::~Base()//成员函数实现 { cout; } class Derived:public Base { public: Derived(); ~Derived(); private: int *p; }; Derived::Derived() { p=new int(0);//从堆上分配一个int型...

    C++虚析构函数、纯虚析构函数

    排除这个问题非常简单:给基类一个虚析构函数。于是,删除一个派生类对象的时候有了你所期望的正确行为。将销毁整个对象,包括全部的派生类部分。  但是,一般如果不做基类的类的析构函数一般不声明为

    全面解读C#编程中的析构函数用法

    析构函数用于析构类的实例。 备注 不能在结构中定义析构函数。只能对类使用析构函数。 一个类只能有一个析构函数...这样,前面的析构函数代码被隐式地转换为以下代码: protected override void Finalize() { try {

    C++中确定基类有虚析构函数

    本文给大家介绍了C++中确定基类有虚析构函数的方法。

    浅谈C++基类的析构函数为虚函数

     在实现多态时, 当用基类指针操作派生类, 在析构时候防止只析构基类而不析构派生类。 2、例子:  (1)、    #include  using namespace std;  class Base{  public: Base() {};  ~Base() {cout &lt;&...

    详解C++中虚析构函数的作用及其原理分析

    也就说虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的. 我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要...

    从汇编看c++的默认析构函数的使用详解

    c++中,如果没有为一个类提供析构函数,那么...2 类继承自一个基类,基类含有自定义析构函数(如果基类没有自定义析构函数,但是编译器会为它提供一个非无用的默认析构函数,也属于这种情况。这就是说,只要基类含有一

    java kok 构造与析构函数

    进入全屏解码显示时间需要2S以上时间,在屏幕中央显示提示:"正在读取图片,请稍侯…

    定义基类person,数据成员name,sex,age,构造函数,析构函数,输出name,sex,age的函数display()。②定义公有派生类student

    定义基类person,数据成员name,sex,age,构造函数,析构函数,输出name,sex,age的函数display()。②定义公有派生类student,数据成员:num,构造函数,析构函数,输出name,sex,age,num的函数display()。③ ...

    C++ 课程作业 继承与派生 (motorcycle类设计(虚基类))

    声名一个基类vehicle,有私有成员maxspeed和weight,公有成员run...注意:构造函数和析构函数中均为cout语句,说明哪个构造/析构函数被调用。 该题重点和难点在于构造函数的设计,需考虑怎么给基类及最远基类传递参数。

    类中的函数分类与虚函数的原理

    析构函数:当[DELETE]类指针时 非虚会从[此类]一直释放到基类,为虚时会从被赋于的[NEW类]的析构函数一直释放到基类. 总得来说释放[方向]都是从[子类]到[父类],只是开始释放的位置不一样.这就是为什么基类的析构函数...

    C++中构造函数与析构函数的调用顺序详解

    在一般情况下,调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。 简单来说,其构造函数...

    c++思维导图/很全,附带考点

    2.用基类类型指针绑定派生类实例,析构的时候,如果基类析构函数不是虚函数,则只会析构基类,不会析构派生类对象,从而造成内存泄漏。 ●C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,...

    c++ 习题 派生类和继承

    含有虚基类的派生类的构造函数有什么要求,什么是最远派生类,建立一个含有虚基类的派生类的对象时,为什么由最远派生类的构造函数负责虚基类的初始化?继承与组合之间的区别与联系是什么?派生类的构造函数是怎样的...

    多继承与虚基类

    1, 设计一个人员基类person类,包括描述基本信息的数据成员,提供基本操作的函数成员以及析构函数和不同形式的构造函数

    类的继承与派生实验

    1:掌握单继承和多继承下派生类的定义方法,理解基类成员在...3:理解同名冲突的产生原因,会使用虚基类来解决第三类同名冲突问题,并理解引入虚基类后构造函数、析构函数的调用顺序。 4:理解赋值兼容的相关使用方法。

Global site tag (gtag.js) - Google Analytics