C++构造与析构函数中调用虚函数的问题
前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#if..#end,所以想利用C++的多态性,但是怎么让内存池完好退出却没想到自认为完美的方案。但是一个很偶然的机会想到在基类的析构函数中调用虚函数来做文章,不过又一想,一个类如果有虚函数,那么编译器会在即使没写构造函数的情况下也会生成构造函数,那么如果想类的构造与析构函数中强行调用虚函数会出现什么情况呢?今天来研究一下这个问题。
首先看一段测试代码
#include <stdio.h> class CBaseTest
{
public:
CBaseTest() {PrintTest();}
~CBaseTest() {PrintTest();}
virtual void PrintTest(){printf("CBaseTest::PrintTest\n");}
}; class CTest :public CBaseTest
{
public:
CTest() {PrintTest();}
~CTest() {PrintTest();}
virtual void PrintTest(){printf("CTest::PrintTest\n");}
}; int _tmain(int argc, _TCHAR* argv[])
{
CBaseTest *p = new CTest;
//p->PrintTest();
delete p;
getchar();
return ;
}
这段测试代码的运行结果如下:
CBaseTest::PrintTest
CTest::PrintTest
CBaseTest::PrintTest
显然,~CTest()函数没有执行,是因为析构的时候是按照CBaseTest类型的析构的,如果需要保证子类构造函数执行,那么~CBaseTest()必须也写成析构函数,这也是很多手写COM或者其他设计中常见手法,但是这不在今天讨论之列。
从中我们可以看到,在构造与析构函数中PrintTest的调用并没有形成多态,那么具体是什么原因呢?
其实我们可以从反汇编角度去看,由于构造分两步,先看CBaseTest构造的时候:
再看CTest构造的时候:
从图上我们可以看到,this指针所指的地址0x00a51a60,第一个成员变量也就是虚表地址,在CBaseTest构造的时候填入的是CBaseTest的虚表地址,也就是对应了
mov dword ptr [eax],offset CBaseTest::`vftable' (12267FCh)
这条汇编语句。而在CTest构造函数里面则有这条语句
mov dword ptr [eax],offset CTest::`vftable' (1226740h)
将对象的虚表修改成CTest虚表。也就是说在这种子类继承父类的过程中,如果子类想实现多态的情况,那必须要等到子类构造完成才可以,而父类的构造会将自己的虚表地址填入对象的第一个成员变量也就是对象的前4个字节中去,而不会去管被子类改写后的情况。
事实上父类当然要这么做,因为如果程序员new出来的就是父类,而父类把子类的虚表地址填入自己对象前4个字节那还了得?所以这个光荣的任务理所当然的交给子类自己去完成了。
如果放到析构函数中,虚函数又会是什么表现呢?事实上和上述情况差不多,在这里就不再举例了,感兴趣的同学可以调试一下。
那么现在又有一个问题,如果是纯虚函数会怎么样呢?其实很简单会直接编译不过,因为此时要取自己类中的虚函数执行,而纯虚函数并没有实现,所以编译器会在第一时间报告这个错误。
C++构造与析构函数中调用虚函数的问题的更多相关文章
- EC笔记,第二部分:9.不在构造、析构函数中调用虚函数
9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...
- C++箴言:避免构造或析构函数中调用虚函数
如果你已经从另外一种语言如C#或者Java转向了C++,你会觉得,避免在类的构造函数或者析构函数中调用虚函数这一原则有点违背直觉.但是在C++中,违反这个原则会给你带来难以预料的后果和无尽的烦恼. 正 ...
- C++-不要在构造和析构函数中调用虚函数
在实习的单位搞CxImage库时不知为什么在Debug时没有问题,但是Release版里竟然跳出个Pure virtual function call error! 啥东西呀,竟然遇上了,就探个究竟吧 ...
- C++ 笔记(二) —— 不要在构造和析构函数中调用虚函数
ilocker:关注 Android 安全(新手) QQ: 2597294287 class Transaction { //所有交易的 base class public: Transaction( ...
- 【校招面试 之 C/C++】第10题 C++不在构造函数和析构函数中调用虚函数
1.不要在构造函数中调用虚函数的原因 在概念上,构造函数的工作是为对象进行初始化.在构造函数完成之前,被构造的对象被认为“未完全生成”.当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数, ...
- 在构造函数和析构函数中调用虚函数------新标准c++程序设计
在构造函数和析构函数中调用虚函数不是多态,因为编译时即可确定调用的是哪个函数.如果本类有该函数,调用的就是本类的函数:如果本类没有,调用的就是直接基类的函数:如果基类没有,调用的就是间接基类的函数,以 ...
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...
- c++有关构造函数和析构函数中调用虚函数问题
今天看了一道迅雷的笔试题目,然后引起一段思考,题目如下: 下列关于虚函数的说法正确的是()A.在构造函数中调用类自己的虚函数,虚函数的动态绑定机制还会生效.B.在析构函数中调用类自己的虚函数,虚函数的 ...
- 09——绝不在构造和析构函数中调用virtual函数
在base class构造期间,virtual函数不是virtual函数. 构造函数.析构函数中不要调用virtual函数.
随机推荐
- log4j.rootLogger
log4j.rootLogger=INFO, FILE, CONSOLE log4j.rootLogger=TRACE, FILE, CONSOLE
- Theorem、Proposition、Lemma和Corollary等的解释与区别
Theorem:定理.是文章中重要的数学化的论述,一般有严格的数学证明. Proposition:可以翻译为命题,经过证明且interesting,但没有Theorem重要,比较常用. Lemma:一 ...
- 人脸识别--Open set和Close set的区别
训练和测试人脸识别分类器时,总会提到Open-set和Close-set.这俩词到底是什么概念呢?有什么区别呢? 所谓close-set,就是所有的测试集都在训练集中出现过.所以每次的预测直接得出测试 ...
- C++之控制内存分配
一.内存分配方式 在C++中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区.栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释 ...
- Android开发 AndroidStudio解决Error:moudle not specified
问题描述 在使用Android Studio 进行Builder APKs的时候,如果发现无法degub, 进行配置的时候 没有module可以进行指定 问题原因 项目未与Grade Files 文件 ...
- vagrant centos lamp小记
更新包 sudo yum -y update vagrant centos 默认语言好像是德语,看不懂,需要更换为 en_US [vagrant@localhost ~]$ locale LANG=d ...
- vue better-scroll 下拉上拉,加载刷新
_initScroll(){ this.$nextTick(() => { if (!this.scroll) { ...
- 2019亚太内容分发大会,阿里云获CDN领袖奖、技术突破奖
近日,亚太CDN产业联盟主办的2019亚太内容分发大会在上海召开.本次大会以"5G分发"为主题,集结了CDN领域近千名行业领袖.专家参与.在会上,阿里云斩获“CDN领袖奖”.“技术 ...
- MFC基础类及其层次结构
从类CCmdTarget派生出绝大多数MFC中的类,其层次结构如下图: 从根类Cobject层层派生出绝大多数MFC中的类,层次结构如下图: MFC中重点类: CObject类是MFC的绝大部分类的基 ...
- memcpy函数实现中的优化
今天浏览Google面试题的时候,有看到一个memcpy的实现,以及如何去优化memcpy. 我对memcpy的实现的记忆就是,拷贝的时候需要从后往前拷贝,为何防止内存重叠. 但是如果去优化它我没有想 ...