c 析构函数的调用顺序序和为什么要声明为虚函数

为什么析构函数中也不能使用虚函数?
[问题点数:30分]
为什么析构函数中也不能使用虚函数?
[问题点数:30分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2013年7月 C/C++大版内专家分月排行榜第一
2015年9月 C/C++大版内专家分月排行榜第二2013年6月 C/C++大版内专家分月排行榜第二
2013年6月 C/C++大版内专家分月排行榜第三
2016年2月 C/C++大版内专家分月排行榜第三2016年1月 C/C++大版内专家分月排行榜第三
2016年2月 C/C++大版内专家分月排行榜第三2016年1月 C/C++大版内专家分月排行榜第三
匿名用户不能发表回复!|析构函数的调用顺序和为什么要声明为虚函数
1. 为什么析构函数要声明为虚函数?
因为在继承中使用多态来创建动态对象时, 比如下面的例子:
virtual ~a(){...};
class b : public a
virtual ~b(){...};
a *pa = new b();
由于pa是个基类的指针, 只能识别属于基类的部分, 所以如果没有虚析构函数的话, 那么子类中特有的部分就不会被释放,
造成"经典"的释放一半, 泄露一半的内存泄露.
2. 析构函数的调用顺序
析构函数的调用和构造函数是类似的,只是顺序正好相反。当调用构造函数时,总是先调用基类的构造函数,再调用派生类的构造函数。当调用析构函数时,总是先调用派生类的析构函数,再调用基类的析构函数。
当有多继承时,例如:
class Base1 {};
class Base2 {};
class Child:public Base1,Base2{};
当child析构时,先调用~Base2(),再调用~Base1()。标准规定了此顺序,构造时按基类列表从左到右,析构时则是从右到左。
c++ 3.0标准文档描述如下:
Bases and members are destroyed in the reverse order of the
completion of their constructor (see 12.6.2)
Initialization shall proceed in the following order:
— First, and only for the constructor of the most derived class as
described below, virtual base classes shall be initialized in the
order they appear on a depthfirst lefttoright traversal of the
directed acyclic graph of base classes, where “lefttoright”is the
order of appearance of the base class names in the derived class
basespecifierlist.
— Then, direct base classes shall be initialized in declaration
order as they appear in the base specifier list (regardless of the
order of the meminitializers).
— Then, nonstatic data members shall be initialized in the order
they were declared in the class definition (again regardless of the
order of the meminitializers).
— Finally, the body of the constructor is executed.
[Note: the declaration order is mandated to ensure that base and
member subobjects are destroyed in the reverse order of
initialization. ]
材料来源:
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)为什么有了虚析构函数,就能先调用子类的析构函数
虚函数表的理论已经大概明白了,但是还是搞不懂,在有虚析构函数的情况下,为什么delete父类的指针,会先找到子类的析构函数。如果是一般的虚函数,因为父类和子类的函数名字都是一样的,调用该函数的时候,通过虚函数表,就会找到子类的函数地址,调用相应的函数。但是虚析构函数,父类和子类的函数名字是不同的,这样我就想不明白了,delete父类的指针,应该会去找父类的析构函数啊,怎么会先去子类的虚函数表中找到子类的析构函数呢?
写下你的评论...
兄弟,你没明白我的意思。我不理解的是为什么有了虚析构函数,就能调用到子类的析构函数。这个你了解不?
写下你的评论...
写下你的评论...
写下你的评论...
Copyright (C) 2018 imooc.com All Rights Reserved | 京ICP备 号-11博客访问: 266492
博文数量: 164
博客积分: 0
博客等级: 民兵
技术积分: 1189
注册时间:
分类: C/C++ 10:55:39
注:本文内容来源于zhice163博文,感谢作者的整理。
1.为什么基类的析构函数是虚函数?
  在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。
  下面转自网络:源地址&
  a.第一段代码
#include using namespace class ClxBase{ public:
ClxBase() {}; ~ClxBase() {cout << "Output from the destructor of class ClxBase!" <<}; void DoSomething() { cout << "Do something in class ClxBase!" << };
}; class ClxDerived : public ClxBase{ public:
ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << }; void DoSomething() { cout << "Do something in class ClxDerived!" << };
}; int main(){
ClxDerived *p = new ClxD
p->DoSomething();
  运行结果:
  Do something in class ClxDerived!&&&&&&&&&&&&
  Output from the destructor of class ClxDerived!
  Output from the destructor of class ClxBase!&&
  这段代码中基类的析构函数不是虚函数,在main函数中用继承类的指针去操作继承类的成员,释放指针P的过程是:先释放继承类的资源,再释放基类资源.&
  b.第二段代码
#include using namespace class ClxBase{ public:
ClxBase() {}; ~ClxBase() {cout << "Output from the destructor of class ClxBase!" <<}; void DoSomething() { cout << "Do something in class ClxBase!" << };
}; class ClxDerived : public ClxBase{ public:
ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << }; void DoSomething() { cout << "Do something in class ClxDerived!" << }
}; int main(){
ClxBase *p = new ClxD
p->DoSomething();
  输出结果:
  Do something in class ClxBase!
  Output from the destructor of class ClxBase!
&&&&这段代码中基类的析构函数同样不是虚函数,不同的是在main函数中用基类的指针去操作继承类的成员,释放指针P的过程是:只是释放了基类的资源,而没有调用继承类的析构函数.调用  dosomething()函数执行的也是基类定义的函数.
&&&&一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏.
&&&&在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员.如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数.
&&&&析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的.&
  c.第三段代码:
#include using namespace class ClxBase{ public:
ClxBase() {}; virtual ~ClxBase() {cout << "Output from the destructor of class ClxBase!" <<}; virtual void DoSomething() { cout << "Do something in class ClxBase!" << };
}; class ClxDerived : public ClxBase{ public:
ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << }; void DoSomething() { cout << "Do something in class ClxDerived!" << };
}; int main(){
ClxBase *p = new ClxD
p->DoSomething();
  运行结果:
  Do something in class ClxDerived!
  Output from the destructor of class ClxDerived!
  Output from the destructor of class ClxBase!
&&&&这段代码中基类的析构函数被定义为虚函数,在main函数中用基类的指针去操作继承类的成员,释放指针P的过程是:只是释放了继承类的资源,再调用基类的析构函数.调用dosomething()函数执行的也是继承类定义的函数.&&
&&&&如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间.所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数.
下面转自:http://blog.sina.com.cn/s/blog_016ri2.html
1,从存储空间角度
&&&&虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。
2,从使用角度
&&&&&&&&虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。
虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
3、构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它。但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
4、从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数&&
&&从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数
5、当一个构造函数被调用时,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。 当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码- -既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。
&&&&所以它使用的V P T R必须是对于这个类的V TA B L E。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, V P T R将 保持被初始化为指向这个V TA B L E, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置V P T R指向它的 V TA B L E,等.直到最后的构造函数结束。V P T R的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。
&&&&&&&&但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置V P T R指向它自己的 V TA B L E。如果函数调用使用虚机制,它将只产生通过它自己的V TA B L E的调用,而不是最后的V TA B L E(所有构造函数被 调用后才会有最后的V TA B L E)。
阅读(432) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
请登录后评论。

我要回帖

更多关于 析构函数调用虚函数 的文章

 

随机推荐