`
Sweblish
  • 浏览: 100994 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

析构函数为什么是virtual类型的

阅读更多
class CObject
{
   public:
// Object model (types, destruction, allocation)

   virtual CRuntimeClass* GetRuntimeClass() const;
   virtual ~CObject(); //virtual destructors are necessary

   ...
   ...
};

为什么MFC的编写者认为virtual destructors are necessary
(虚拟的析构函数是必要的)?

在著名的VC教程 "精通Visual C++ for Windows 95/NT"(电子工业版,
1997年5月版,胡俭,丘宗明等著)第99页中有这样一段话:

“如果CObject的析构函数不是虚拟的,派生类就不会自动地得到虚拟的
  析构函数,当对象撤消时就会带来问题——只有当前类的析构函数得到
  调用而基类的析构函数就得不到调用。...”

我认为这段解释是这本很不错的书中一个不应出现的严重错误。其意思是说:
若:
class CBase
{
   public:
      ~CBase() { ... };
   ...
};

class CChild : public CBase
{
   public:
      ~CChild() { ... };
   ...
};

main()
{
   Child c;
   ...
   return 0;
}

上段代码在运行时,当栈框中的自动对象 c 被撤消时,只调用~CChild(),
而不调用~CBase()。

我想但凡对C++继承性理论有所了解的人都会立刻指出这是错误的。

由于在生成CChild对象c时,实际上在调用CChild类的构造函数之前必须首先
调用其基类CBase的构造函数,所以当撤消c时,也会在调用CChild类析构函数
之后,调用CBase类的析构函数(析构函数调用顺序与构造函数相反)。也就是说,

无论析构函数是不是虚函数,派生类对象被撤消时,肯定会依次上调其基类的
析构函数。

那么为什么CObject类要搞一个虚的析构函数呢?

仍以上面代码为例,如果main()中有如下代码:
...

CBase * pBase;
CChild c;
pBase = &c;

...

那么在、当pBase指针被撤消时,调用的是CBase的析构函数还是CChild的呢?
显然是CBase的(静态联编)。但如果把CBase类的析构函数改成virtual型,当
pBase指针被撤消时,就会先调用CChild类构造函数,再调用CBase类构造函数。

在这个例子里,所有对象都存在于栈框中,当离开其所处的作用域时,该对象
会被自动撤消,似乎看不出什么大问题。但是试想,如果CChild类的的构造函数

在堆中分配了内存,而其析构函数又不是virtual型的,那么撤消pBase时,将不

调用CChild::~CChild(), 从而不会释放CChild::CChild()占据的内存,造成内存

泄露。

而将CObject的析构函数设为virtual型,则所有CObject类的派生类的析构函数都

自动变为virtual型,这保证了在任何情况下,不会出现由于析构函数未被调用而
导致
的内存泄露。这才是MFC将CObject::~CObject()设为virtual型的真正原因。
分享到:
评论

相关推荐

    华为C++笔试题全部汇总

    delete p[由于p被声明成父类指针,并且父类和子类的析构函数都非虚,因此delete操作只能根据p指针声明的类型来调用父类的析构函数]; std::cout ; } 6.请问下面这段程序的输出结果是_A_: A. 2,1, B. 2,2, C. 1,1,...

    c#学习笔记——学习心得

    当某个类的实例被认为不再有效并符合析构条件时,.NET Framework类库的垃圾回收功能就会调用该类的析构函数实现垃圾回收,一个类只能有一个析构函数。一般准则是,除非有迫不得已的原因,不要使用析构函数,而应把...

    面向对象与C++试题.doc

    系统首先为该动态对象调用析构函数,再释放其占用的内存 D.系统首先释放动态对象占用的内存,再为其调用析构函数 11、可在类外用p.a的形式访问派生类对象 p的基类成员a,其中a是( )。 A.私有继承的公用成员 B....

    C++复习资料之系列

    设有函数关系为y= ,下面选项中能正确表示上述关系为( c )。 (a) y = 1; (b) y = -1; if( x>=0 ) if( x!=0) if( x==0 )y=0; if( x>0 )y = 1; else y = -1; else y = 0 (c) if( x) (d) y = -...

    摩托罗拉C++面试题

    子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数等等 15.为什么要引入抽象基类和纯虚函数? 主要目的是为了实现一种接口的效果。 16.介绍一下模板和容器。如何...

    网狐6.6 服务器源代码 & 共享组件 & 数据库 (2)

    //析构函数 virtual ~CSkinResource(); //基础接口 public: //释放对象 virtual VOID __cdecl Release() { return; } //接口查询 virtual VOID * __cdecl QueryInterface(REFGUID Guid, DWORD dwQueryVer); ...

    新手学习C++入门资料

    main() //C++中main()函数默认为int型,而C语言中默认为void型。 { int a; cout; cin>>a; /*输入一个数值*/ cout; //输出并回车换行 return 0; } cin,cout,endl对象,他们本身并不是C++语言的组成部分。...

    Effective+C+++3rd+chm+中文版(代码加亮)

    destructor(析构函数)什么时候应该是 virtual(虚拟)的?当 operator new(运算符 new)找不到足够的内存时它应该怎么办?类似这些的令人费神的细节是至关重要的,因为错误的做法几乎总是导致无法预料的,很可能...

    网狐6.6 服务器源代码 & 共享组件 & 数据库 (1)

    //析构函数 virtual ~CPlatformResource(); //基础接口 public: //释放对象 virtual VOID __cdecl Release() { delete this; } //接口查询 virtual VOID * __cdecl QueryInterface(REFGUID Guid, DWORD ...

    Visual C# 2010程序设计教程(教程PPT+源代码)

    5.3 构造函数与析构函数 5.3.1 构造函数 5.3.2 析构函数 5.4 本章小结 5.5 上机练习 5.6 习题 第6章 域、属性与事件 6.1 域 6.1.1 域的初始化 6.1.2 只读域与readonly关键字 6.2 属性 6.2.1 属性的声明 6.2.2 属性...

    C++ Primer第四版【中文高清扫描版】.pdf

    15.4.4 虚析构函数 495 15.4.5 构造函数和析构函数中的虚函数 497 15.5 继承情况下的类作用域 497 15.5.1 名字查找在编译时发生 498 15.5.2 名字冲突与继承 498 15.5.3 作用域与成员函数 499 15.5.4 虚函数与作用域 ...

    C++中虚函数和纯虚函数定义

     一个成员函数被声明为虚函数后,在同一类族中的类不能再定义一个非virtual的但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数。  根据什么考虑是否把一个成员函数声明为虚函数?  ①...

    EffectiveCPlusPlus

    多态基类声明virtual析构函数 别让异常逃离析构构函数 不在构造析构过程中调用virtual函数 operator =返回对* this的引用 在operator =中处理自我赋值 复制对象时勿忘其每一个部份 以对象管理资源 在资源管理类中...

    Effective C++(第三版)

    条款07:为多态基类声明virtual析构函数 declare destructors virtual in polymorphic base classes. 条款08:别让异常逃离析构函数 prevent exceptions from leaving destructors. 条款09:绝不在构造和析构过程中...

    Java版水果管理系统源码-c-plus-Interview:c-plus-面试

    虚函数的实现原理,子类构造析构函数的调用顺序 虚函数怎么实现的 虚函数机制 虚函数的执行和类成员函数的执行在汇编的级别差了哪些指令 虚函数的作用和实现原理 构造中能不能调虚函数 析构中能不能调虚函数 C++模板...

    C++大学教程,一本适合初学者的入门教材(part2)

    6.13 何时调用构造函数与析构函数 6.14 使用数据成员和成员函数 6.15 微妙的陷阱:返回对Private数据成员的引用 6.16 通过默认的成员复制进行赋值 6.17 软件复用性 6.18 有关对象的思考:编写电梯模拟程序的类...

    Effective C++ 中文版

    条款07:为多态基类声明Virtual析构函数 条款08:别让异常逃离析构函数 条款09:绝不在构造和析构过程中调用Virtual函数 条款10:令Operator=返回一个referenceto this 条款11:在Operator=中处理“自我赋值” ...

    C++大学教程,一本适合初学者的入门教材(part1)

    6.13 何时调用构造函数与析构函数 6.14 使用数据成员和成员函数 6.15 微妙的陷阱:返回对Private数据成员的引用 6.16 通过默认的成员复制进行赋值 6.17 软件复用性 6.18 有关对象的思考:编写电梯模拟程序的类...

    传智播客_C++基础课程讲义_v1.0.7

    面试题8:为什么要定义虚析构函数? 6 其他 6 4.3多态原理探究 6 4.3.1 多态的实现原理 6 4.3.2如何证明vptr指针的存在 6 4.3.3构造函数中能调用虚函数,实现多态吗 6 5、纯虚函数和抽象类 6 5.1基本概念 6 5.2抽象...

    C++上机实验报告-实验六.docx

    析构函数 Point(){} Point(int,int); ~Point(){} //++.--重载 Point& operator ++(); Point operator ++(int); Point& operator --(); Point operator --(int); //输出点坐标 void showPoint(); }; Point::Point(int...

Global site tag (gtag.js) - Google Analytics