环境:XPSP3 VS2005

今天黑总给应聘者出了一个在C++的构造函数中调用虚函数的问题,具体的题目要比标题复杂,大体情况可以看如下的代码:

  1. class Base
  2. {
  3. public:
  4. Base()
  5. {
  6. Fuction();
  7. }
  8. virtual void Fuction()
  9. {
  10. cout << "Base::Fuction" << endl;
  11. }
  12. };
  13. class A : public Base
  14. {
  15. public:
  16. A()
  17. {
  18. Fuction();
  19. }
  20. virtual void Fuction()
  21. {
  22. cout << "A::Fuction" << endl;
  23. }
  24. };
  25. // 这样定义一个A的对象,会输出什么?
  26. A a;

首先回答标题的问题,调用当然是没有问题的,但是获得的是你想要的结果吗?或者说你想要什么样的结果?

有人说会输出:

  1. A::Fuction
  2. A::Fuction

如果是这样,首先我们回顾下C++对象模型里面的构造顺序,在构造一个子类对象的时候,首先会构造它的基类,如果有多层继承关系,实际上会从最顶层的基类逐层往下构造(虚继承、多重继承这里不讨论),如果是按照上面的情形进行输出的话,那就是说在构造Base的时候,也就是在Base的构造函数中调用Fuction的时候,调用了子类A的Fuction,而实际上A还没有开始构造,这样函数的行为就是完全不可预测的,因此显然不是这样,实际的输出结果是:

  1. Base::Fuction
  2. A::Fuction

据说在Java中是上一种输出(感觉有点匪夷所思)。

我们来单步看一下到底发生了什么?在A的构造函数里面首先会去调用Base的构造函数,Base的构造函数如下:

class Base
{
public:
 Base()
00411600  push        ebp  
00411601  mov         ebp,esp 
00411603  sub         esp,0CCh 
00411609  push        ebx  
0041160A  push        esi  
0041160B  push        edi  
0041160C  push        ecx  
0041160D  lea         edi,[ebp-0CCh] 
00411613  mov         ecx,33h 
00411618  mov         eax,0CCCCCCCCh 
0041161D  rep stos    dword ptr es:[edi] 
0041161F  pop         ecx  
00411620  mov         dword ptr [ebp-8],ecx 
00411623  mov         eax,dword ptr [this] 
00411626  mov         dword ptr [eax],offset Base::`vftable' (41770Ch)
 {
  Fuction();
0041162C  mov         ecx,dword ptr [this] 
0041162F  call        Base::Fuction (4111A9h)

}
00411634  mov         eax,dword ptr [this] 
00411637  pop         edi  
00411638  pop         esi  
00411639  pop         ebx  
0041163A  add         esp,0CCh 
00411640  cmp         ebp,esp 
00411642  call        @ILT+460(__RTC_CheckEsp) (4111D1h) 
00411647  mov         esp,ebp 
00411649  pop         ebp  
0041164A  ret

从单步跟踪来看,注意黑色加粗的那部分汇编代码,ecx中存放的是对象的地址(0x0012ff60,我的机器上的情况看下图,有图有真相),首先是设置vtable的地址到对象的前四个字节(不同的编译器可能不同),然后就直接调用了Base::Fuction函数,并没有走虚机制,而我们此时看虚表中的状态,虚表已经填充的是0x4111a9,注意虚表的地址0x0041770c,而此时对象地址0x0012FF60前四个字节存放的正是0x0041770c。

        继续跟踪,流程又回到A的构造函数中,再次注意加粗部分的代码,从基类Base的构造函数返回后,在A的构造函数中,重设了虚表指针,现在的虚表指针是(0x417700h),同样调用Fuction的时候直接调用了A::Fuction函数,并没有使用虚机制,而且此时虚表0x417700h指向的位置存放的0x41110e正是A::Fuction的地址。

class A : public Base
{
public:
 A()
00411590  push        ebp  
00411591  mov         ebp,esp 
00411593  sub         esp,0CCh 
00411599  push        ebx  
0041159A  push        esi  
0041159B  push        edi  
0041159C  push        ecx  
0041159D  lea         edi,[ebp-0CCh] 
004115A3  mov         ecx,33h 
004115A8  mov         eax,0CCCCCCCCh 
004115AD  rep stos    dword ptr es:[edi] 
004115AF  pop         ecx  
004115B0  mov         dword ptr [ebp-8],ecx 
004115B3  mov         ecx,dword ptr [this] 
004115B6  call        Base::Base (411140h) 
004115BB  mov         eax,dword ptr [this] 
004115BE  mov         dword ptr [eax],offset A::`vftable' (417700h)
 {
  Fuction();
004115C4  mov         ecx,dword ptr [this] 
004115C7  call        A::Fuction (41110Eh)
 }
004115CC  mov         eax,dword ptr [this] 
004115CF  pop         edi  
004115D0  pop         esi  
004115D1  pop         ebx  
004115D2  add         esp,0CCh 
004115D8  cmp         ebp,esp 
004115DA  call        @ILT+460(__RTC_CheckEsp) (4111D1h) 
004115DF  mov         esp,ebp 
004115E1  pop         ebp  
004115E2  ret

其实事情就是这么简单。

http://blog.csdn.net/magictong/article/details/6734241

C++中构造函数能调用虚函数吗?(答案是语法可以,输出错误),但Java里居然可以的更多相关文章

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

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

  2. C++ Daily 《3》----构造函数可否是虚函数

    C++ 中构造函数可否是虚函数? 绝不要!! 而且,在构造函数中调用虚函数也是不提倡的行为,因为会引发预想不到的结果. 因为,在 derived class 对象构造的过程中,首先调用的是基类的构造函 ...

  3. C++构造函数中不能调用虚函数

    在构造函数中调用虚函数,并不会产生多态的效果,就跟普通函数一样. c++ primer 第四版中497页15.4.5构造函数和析构中的虚函数讲到,如果在构造函数或析构函数中调用虚函数,则运行的是为构造 ...

  4. 关于在C#中构造函数中调用虚函数的问题

    在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...

  5. C++ 构造函数中调用虚函数

    我们知道:C++中的多态使得可以根据对象的真实类型(动态类型)调用不同的虚函数.这种调用都是对象已经构建完成的情况.那如果在构造函数中调用虚函数,会怎么样呢? 有这么一段代码: class A { p ...

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

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

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

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

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

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

  9. C++中为什么构造函数不能是虚函数,析构函数是虚函数

    一, 什么是虚函数? 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语 ...

随机推荐

  1. Java中sleep()与wait()区别

    学习时正好碰到这两个方法,就查阅相关资料,并通过程序实现,进行区别一下: 1.每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块.sleep() ...

  2. [.NET Core 24]把project.json迁移到.csproj

    链接:https://blog.jetbrains.com/dotnet/2017/04/04/rider-eap-update-csproj-based-net-core-support-migra ...

  3. 轻松学习JavaScript十八:DOM编程学习之DOM简单介绍

    一DOM概述 DOM(文档对象模型)是HTML和XML的应用程序接口(API).DOM将把整个页面规划成由节点层级构成的文档. DOM描绘了一个层次化的节点树,执行开发者加入,移除和改动页面的某一部分 ...

  4. 对多线程java内存模型JMM

    多线程概念的引入体现了人类重新有效压力寨计算机.这是非常有必要的,由于所涉及的读数据的过程中的一般操作,如从磁盘.其他系统.数据库等,CPU计算速度和数据读取速度已经严重失衡.假设印刷过程中一个线程将 ...

  5. 一起学Python:字典介绍

    字典介绍 想一想: 如果有列表 nameList = ['xiaoZhang', 'xiaoWang', 'xiaoLi']; 需要对"xiaoWang"这个名字写错了,通过代码修 ...

  6. 【t006】三角形分形描绘问题

    Time Limit: 1 second Memory Limit: 50 MB [问题描述] 分形是以多种概念和方法相互冲击融合为特征的图形.分形所呈现的无穷玄机和美感引发人们去探索.分形使人们觉悟 ...

  7. AndroidStudio 3.0 生成jar包的方法

    AndroidStudio3.0生成jar包的方法,一下将逐步演示流程,及步骤讲解 1:新建样例工程,目录如下 2:鼠标右键点击app->New->选择Module 选择AndroidLi ...

  8. Spring异步方法注解 @Async

    @Async 0. 介绍 在Spring 3.x之后,通过内置@Async标明异步方法,可以简化异步开发流程. @Async既可提供无返回值的调用,也可提供有返回值的调用,下文将分别介绍两种使用方式. ...

  9. android (12) Fragment使用

    一.Fragment概要: 于Fragment经,布局更好地适应各种尺寸的android打电话,加方便的实现不同页面的切换,就不像曾经activity的跳转那样的麻烦了.能够在activity中嵌套不 ...

  10. jQuery在线选座订座(高铁版)

    除了电影院在线选座,我们还会接触到飞机机舱选座,当然也有汽车票火车票选座的.假如有一天买火车票也提供在线选座,那么今天我来给大家介绍下如何使用jQuery选座插件完成高铁列车座位布置.选座.不同等级座 ...