关于 虚析构函数.

Vegertar 2008-07-12 08:38:35
这是书上的原话:

当派生类的对象从内存中撤消时通常都会调用派生类的析构函数,然后再调用基类的析构函数.但是,假如对象是用
new 运算符声明的,而指针类型是带有虚拟析构函数的基类类型,这时情况会如何呢?
当程序用带指针参数的 delete 运算符撤消对象时,这时会执行基类的析构函数,而不是派生类的析构函数.
......
当基类的析构函数为虚函数时,不论指针指的是何种类型,编译器都会调用正确的析构函数.

为什么会有这样的现象呢,只要基类的析构函数是虚函数,delete运算符撤消对象时就能为动态分配的对象调用正确的
析构函数.怎么都想不通.
...全文
158 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
oywoywoyw 2008-10-10
  • 打赏
  • 举报
回复
最喜欢6楼的回答方式,谢谢。
minichow 2008-07-13
  • 打赏
  • 举报
回复
看看RTTI
Vegertar 2008-07-13
  • 打赏
  • 举报
回复
虚数指针表啊,知道有这么个概念,不过不太懂,现在去看看.
那么基本情况就是这样子了,结贴了.多谢各位指点.
angelcm51 2008-07-12
  • 打赏
  • 举报
回复
虚拟函数的引入是为了实现多态性(polymorphism),而虚拟析构函数是怎么实现多态性的?我想通过以下例子进行说明。

如果基类的析构函数不是虚拟的,那么派生类的析构函数将不会被调用。
虚拟析构函数的作用是确保实例化的对象能够调用自己类实现的虚构函数而被完整的虚构(释放).当通过基类的指针或引用去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的,派生类的析构函数永远不会被调用.如:
#i nclude <iostream>
using namespace std;

class A
{
public:
A(){
cout << "A construction" << endl;
}
virtual ~A() //注意这里
{
cout << "A destruction" << endl;
}
};

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

void main(){
{
B pg;
}
cout << "---------------------------" << endl;
{
A *pe = new B( );
delete pe;
}

}


程序运行结果:

A construction
B construction
B destruction
A destruction
---------------------------
A construction
B construction
B destruction
A destruction

如果去掉 virtual 结果为:

A construction
B construction
B destruction
A destruction
---------------------------
A construction
B construction
A destruction
matrixdwy 2008-07-12
  • 打赏
  • 举报
回复
关键:虚表
LZ看的书里面没说虚表?
K行天下 2008-07-12
  • 打赏
  • 举报
回复
你没有理解虚拟析构函数和多态
new 运算符声明的,而指针类型是带有非虚拟析构函数的基类类型,这时情况会如何呢?
这样delete的时候将不会调用派生类的析构函数

什么原因呢?
因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针(如指向虚析构函数)
派生类也会继承这个虚函数表,如果派生类改变了虚函数,就会更新这个表的指针,指向更新
后的函数(新的虚构函数)。 所以你使用 base* = new derived;然后调用delete base,base指向的类对象里面保存的虚函数指针保证了正确的析构

这其实是多态的实现途径!
iu_81 2008-07-12
  • 打赏
  • 举报
回复
试试这个简单的:类应该有虚析构函数,除非这个类没有虚函数。原理:如果有虚函数,说明你想通过基类指针来使用派生对象,并且你所可能做的事情之中,可能包含了调用析构函数(通常通过delete隐含完成)。一旦你在类中加上了一个虚函数,你就已经需要为每一个对象支付空间代价(每个对象一个指针;注意这是理论上的编译器特性;实际上每个编译器都是这样做的),所以这时使析构函数成为虚拟的通常不会额外付出什么。
weiyijiji 2008-07-12
  • 打赏
  • 举报
回复
多态
iu_81 2008-07-12
  • 打赏
  • 举报
回复
虚析构函数

如果你正在使用new和delete处理个体对象(而非数组),那么可能会出现另外一种未定义行为。例如,考虑下面的这些类:

class Base { };
class Derived: public Base { };

假设我们创建一个Derived类型的对象并将其地址放入一个指向Base类型的指针中:

Base* bp = new Derived;

那么稍后我们就可以利用该指针来释放对象:

delete bp;

首先要注意的是,在这个例子中没有使用[]是正确的,因为我们并非在处理一个数组。然而,这儿存在另外一个问题:指针bp指向的对象的类型和创建时的类型不一样,那么通过它来delete对象安全吗?

关于这一点语言标准说得很清楚:如果Base具有一个虚拟析构函数,那么这样的delete的行为就具有良好的定义,否则就不然。但是,我们听过有人声称,只要类Derived不存在需要析构的成员,这样的delete就是可以接受的。这个虽不正确但似乎可信的理由在于,编译器将利用指针的类型(此处是Base)来决定删除什么样的成员,而这种决策在Derived没有额外的成员时是无害的。

关于这个理由为什么不正确的最直接的论据就是它跟语言标准说的不一样,但这么说难以令人满意,因为这并没有解释清楚语言标准为什么要那么做。

如果我们为Derived添加一个需要析构的成员然后再看看发生了什么,我们就会找到一个更加令人满意的论据:

// revised version
class Derived: public Base { string s; };

现在删除bp时编译器应该怎么做呢?如果要求每一个编译器去检查bp是否指向一个Derived对象,那将会强求每一个类都要有一个虚析构函数,即使程序的作者很清楚每一个delete都与相对应的new一致也是如此。预先设置虚析构函数的出发点在于将以后某些情境下的需求提前准备好。因此当删除bp时,不应该要求编译器去清除成员s。

如果编译器不去释放成员s,那么当bp指向Derived对象时删除bp就存在内存泄漏的可能性,因此程序员不可以这么做,那这个错误应该由编译器来检查吗?这同样是一个关于负担的权衡问题:如果要求编译器去检查bp确实是指向一个Base对象,那么编译器也同样能处理“bp指向一个Derived对象”的情况。因此,要求编译器检查此类错误与要求编译器去释放s相比,只会更加低效、更没意义。

一种替代的方法就是要求每个编译器器不要去检查bp所指的对象类型。但是在这种情形下,当bp指向一个Derived对象时删除bp就会发生错误,而该错误又是编译器被禁止去侦测的,即使它原本想去侦测也侦测不了。

因此,对于“在基类没有虚析构函数时通过基类指针来删除派生类对象的程序究竟该怎么办”这一问题,语言标准将其交给编译器自行处理。通过将此类程序的行为方式交由编译器处理,语言标准允许编译器去检查此类错误 — 如果它们想这么做的话,同时也允许不去检查这类问题而将负担最小化 — 如果编译器更喜欢这种方式的话。在你的程序中避免此类问题的最直接了当的方式就是:除非有什么不可抗拒的原因,否则每一个被用作基类的类都应当拥有一个虚析构函数。
lily604 2008-07-12
  • 打赏
  • 举报
回复
这就是C++的动态绑定机制啊!楼主可以主要看看关于虚表的知识。在运行时决定要调用哪个具体的函数。
xkyx_cn 2008-07-12
  • 打赏
  • 举报
回复
很简单:
未定义析构函数为虚函数,使用基类指针delete派生类对象时,其行为是未确定的 out of control
定义了析构函数为虚函数,使用基类指针delete派生类对象时,其行为是确定的 under control
Cpp权哥 2008-07-12
  • 打赏
  • 举报
回复
1、调用虚函数时,系统会根据对象本身最确切的类型来调用那个类中定义的那个函数。
2、对基类指针进行delete时,调用的是基类的析构函数。如果这个析构函数是虚函数,第1条也起作用。
wjb_yd 2008-07-12
  • 打赏
  • 举报
回复
google vptr

64,635

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧