前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#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++构造与析构函数中调用虚函数的问题的更多相关文章

  1. EC笔记,第二部分:9.不在构造、析构函数中调用虚函数

    9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...

  2. C++箴言:避免构造或析构函数中调用虚函数

    如果你已经从另外一种语言如C#或者Java转向了C++,你会觉得,避免在类的构造函数或者析构函数中调用虚函数这一原则有点违背直觉.但是在C++中,违反这个原则会给你带来难以预料的后果和无尽的烦恼. 正 ...

  3. C++-不要在构造和析构函数中调用虚函数

    在实习的单位搞CxImage库时不知为什么在Debug时没有问题,但是Release版里竟然跳出个Pure virtual function call error! 啥东西呀,竟然遇上了,就探个究竟吧 ...

  4. C++ 笔记(二) —— 不要在构造和析构函数中调用虚函数

    ilocker:关注 Android 安全(新手) QQ: 2597294287 class Transaction { //所有交易的 base class public: Transaction( ...

  5. 【校招面试 之 C/C++】第10题 C++不在构造函数和析构函数中调用虚函数

    1.不要在构造函数中调用虚函数的原因 在概念上,构造函数的工作是为对象进行初始化.在构造函数完成之前,被构造的对象被认为“未完全生成”.当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数, ...

  6. 在构造函数和析构函数中调用虚函数------新标准c++程序设计

    在构造函数和析构函数中调用虚函数不是多态,因为编译时即可确定调用的是哪个函数.如果本类有该函数,调用的就是本类的函数:如果本类没有,调用的就是直接基类的函数:如果基类没有,调用的就是间接基类的函数,以 ...

  7. 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数

    关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...

  8. c++有关构造函数和析构函数中调用虚函数问题

    今天看了一道迅雷的笔试题目,然后引起一段思考,题目如下: 下列关于虚函数的说法正确的是()A.在构造函数中调用类自己的虚函数,虚函数的动态绑定机制还会生效.B.在析构函数中调用类自己的虚函数,虚函数的 ...

  9. 09——绝不在构造和析构函数中调用virtual函数

    在base class构造期间,virtual函数不是virtual函数. 构造函数.析构函数中不要调用virtual函数.

随机推荐

  1. iOS开发UIResponder之NSUndoManager

    1.简介 UIResponder有个属性:NSUndoManager @property(nullable, nonatomic,readonly) NSUndoManager *undoManage ...

  2. 【JS】 +function(){} 作用

    原文地址:https://www.jianshu.com/p/a2666014a280 瞎扯 在JS中,经常会遇到下面这种 代码, 到底 在 function 前面加一个 一元操作符, 有什么作用呢? ...

  3. AsyncCallback IAsyncResult

    using System; using System.Threading; using System.Collections.Generic; using System.Windows.Forms; ...

  4. LeetCode刷题笔记-回溯法-括号生成

    题目描述: 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", "( ...

  5. CentOS增加swap分区大小

    来自:http://www.centoscn.com/CentOS/Intermediate/2014/0222/2446.html 1. 查看当前分区情况 free -m 2. 增加 swap 大小 ...

  6. D3.js比例尺 定量比例尺 之 线性比例尺(v3版本)

    定量比例尺 : 数学上有函数的概念,不是编程中所说的函数,如线性函数.指数函数.对数函数等,而指的是一个量随着另一个量的变化而变化.例如有一下线性函数 : y=2x+1该函数在二维坐标系中绘制出来的图 ...

  7. 【ArcObject】 AxTocControl:实现图层可移动

    设置axTocControl属性:EnableLayerDragDrop 为true即可

  8. 两个table合并

    1.两个一样的table合并用Merge函数即可合并(但要求table要有主键id) DataTable1.Merge(DataTable2); 2.没写完,以后继续补充(只有经过笔者验证,能用的才会 ...

  9. C/C++ Microsoft Visual Studio c++ DOC Home

    { // https://docs.microsoft.com/zh-cn/cpp/overview/visual-cpp-in-visual-studio?view=vs-2017 // https ...

  10. thinkphp 模板继承

    模板继承是一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层.模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区 ...