前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#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. ionic:安装

    ylbtech-ionic:安装 1.返回顶部 1. ionic 安装 本站实例采用了ionic v1.3.2 版本,使用的 CDN 库地址: <link href="https:// ...

  2. mac的终端运行ifconfig

    lo0:loopback回环地址一般是127.0.0.0,loopback指本地环回接口(或地址),亦称回送地址().此类接口是应用最为广泛的一种虚接口,几乎在每台路由器上都会使用. gif0: so ...

  3. 全面解析HTML5优缺点

    HTML5是当下最主流的网页标准,它的出现给在线应用和手机游戏开发者带来了不少新机会.基于HTML5,开发者可以制作自己的网络游戏,而这个游戏无 论你在PC.手机还是平板电脑上,无论你用Chrome. ...

  4. Java学习之JVM、JRE、JDK联系与区别

    JVM,全称是Java Virtual Machine,翻译为Java虚拟机: JRE,全称是Java Runtime Environment,翻译为Java运行时环境: JDK,全称是Java De ...

  5. 如何去掉vue路由中的#

    通过脚手架vue-cli构建的项目,在项目启动后,URL地址上都会带有#,如:http://localhost:8080/#/father 原因:这是因为vue-router 默认hash模式, 使用 ...

  6. kcptun搭建

    wget --no-check-certificate https://github.com/kuoruan/shell-scripts/raw/master/kcptun/kcptun.sh sha ...

  7. C 常见字符串操作函数

    头文件 <string.h> 1. char *strstr(const char *str1, const char *str2);      判断str2是否为str1的子串  //s ...

  8. JS 实现省市联动

    使用 JavaScript 实现选择省份,后面联动改变成相应省份下的市 原理很简单: 首先创建两个select下拉框(省.市) 初始化的时候让省都显示出来,市为空  ................. ...

  9. mysql连接卡死,很多线程sleep状态,导致CPU中mysqld占用率极高

    关闭所有 .................................. .连接: ##把全部的MySQL连接kill掉for i in $(mysql -uroot -p123456 -Bse ...

  10. Tomcat - 基本知识

    基本概念 Tomcat是接收和解析http请求,并将结果返回客户端的应用程序 轻量级的web应用服务器 适用于并发性不是很高的系统中 开发和调试jsp的首选 类似的应用程序:Jetty, JBoss/ ...