这个问题来自于《Effective C++》条款9:永远不要在构造函数或析构函数中调用虚函数 。

  假设有如下代码:

    class Transaction {// 所有交易的基类
public:
 Transaction();
 virtual void logTransaction() const = ;//建立依赖于具体交易类型的登录项
 ...
};
Transaction::Transaction() //实现基类的构造函数
{
 ...
 logTransaction(); //最后,登录该交易
}
class BuyTransaction: public Transaction {
// 派生类
public:
 virtual void logTransaction() const; //怎样实现这种类型交易的登录?
 ...
};
class SellTransaction: public Transaction {
//派生类
public:
 virtual void logTransaction() const; //怎样实现这种类型交易的登录?
 ...
};

   现在,请分析执行下列代码调用时所发生的事情:

BuyTransaction b;  

  很明显,一个 BuyTransaction类构造器被调用。但是,首先调用的是Transaction类的构造器-派生类对象的基类部分是在派生类部分之前被构造的。 Transaction构造器的最后一行调用了虚函数logTransaction,但是奇怪的事情正是在此发生的。被调用函数 logTransaction的版本是Transaction中的那个,而不是BuyTransaction中的那个-即使现在产生的对象的类型是 BuyTransaction,情况也是如此。在基类的构造过程中,虚函数调用从不会被传递到派生类中。代之的是,派生类对象表现出来的行为好象其本身就是基类型。不规范地说,在基类的构造过程中,虚函数并没有被"构造"。

  简单的说就是,在子类对象的基类子对象构造期间,调用的虚函数的版本是基类的而不是子类的。

  对上面这种看上去有点违背直觉的行为可以用一个理由来解释-因 为基类构造器是在派生类之前执行的,所以在基类构造器运行的时候派生类的数据成员还没有被初始化。如果在基类的构造过程中对虚函数的调用传递到了派生类, 派生类对象当然可以参照引用局部的数据成员,但是这些数据成员其时尚未被初始化。这将会导致无休止的未定义行为和彻夜的代码调试。沿类层次往下调用尚未初始化的对象的某些部分本来就是危险的,所以C++干脆不让你这样做。

  在对象的析构期间,存在与上面同样的逻辑。一旦一个派生类的析构器运行起来,该对象的派生类数据成员就被假设为是未定义的值,这样以来,C++就把它们当做是不存在一样。一旦进入到基类的析构器中,该对象即变为一个基类对象,C++中各个部分(虚函数,dynamic_cast运算符等等)都这样处理。

  以上是《Effective C++》的部分。其实当基类自子对象构造完毕之后,进入子类对象的构造期间调用的虚函数就是子类覆盖的版本了。即有如下代码所示:

  

    class Base
{
public:
Base()
{
Fuction();
} virtual void Fuction()
{
cout << "Base::Fuction" << endl;
}
}; class A : public Base
{
public:
A()
{
Fuction();
} virtual void Fuction()
{
cout << "A::Fuction" << endl;
}
}; // 这样定义一个A的对象,会输出什么?
A a;

  输出为:

Base::Fuction
A::Fuction

  那么编译器是如何实现的呢?

  在基类子对象构造期间,编译器直接调用基类版本的虚函数。在子类对象构造期间,编译器直接调用基类虚函数的版本。并没有走虚机制。虽然在基类子对象的构造期间,虚表指针指向的是基类的Virtual Table,在进入子类对象构造后,虚表指针指向的是子类对象的Virtual Table。详见http://blog.csdn.net/magictong/article/details/6734241

参考资料:

  1. http://blog.csdn.net/hxz_qlh/article/details/14089895

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

【C++】不要在构造函数或析构函数内调用虚函数的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. C++进阶--构造函数和析构函数中的虚函数

    //############################################################################ /* 任何时候都不要在构造函数或析构函数中 ...

  8. C++:构造函数和析构函数能否为虚函数

    原文:http://blog.csdn.net/xhz1234/article/details/6510568 C++:构造函数和析构函数能否为虚函数? 简单回答是:构造函数不能为虚函数,而析构函数可 ...

  9. C++构造与析构函数中调用虚函数的问题

    前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#if..#end,所以想利用C++的多态性,但是怎么让内存池完好退出却没想到自认为完美的方案.但是一个很偶然 ...

随机推荐

  1. 20155230 实验三《敏捷开发与XP实践》实验报告

    20155230 实验三<敏捷开发与XP实践>实验报告 一.使用工具(Code->Reformate Code)把代码重新格式化 IDEA里的Code菜单有很多实用的功能可以帮助我们 ...

  2. CF 1083 B. The Fair Nut and Strings

    B. The Fair Nut and Strings 题目链接 题意: 在给定的字符串a和字符串b中找到最多k个字符串,使得不同的前缀字符串的数量最多. 分析:  建出trie树,给定的两个字符串就 ...

  3. DSP5509项目之用FFT识别钢琴音调(4)之麦克风输入和Line in输入

    1. 麦克风输入需要修改的内容,之前的版本是LINE IN的输入.实现功能,检测麦克风的输入,并且同时在耳机里面播放. #include <csl.h> #include <csl_ ...

  4. bzoj4998 星球联盟

    bzoj4998 星球联盟 原题链接 题解 先按照输入顺序建一棵树(森林),然后用一个并查集维护联盟的关系,对于不是树上的边\(a-b\),就把\(a-lca(a,b),b-lca(a,b)\)全部合 ...

  5. 简单读取 properties文件

    看了网上很多读取的方法,都太过复杂,直接使用下面的方法就可以简单读取 properties文件了 ide使用idea 测试读取成功 import java.util.ResourceBundle; p ...

  6. C# webapi 路由规则和接收数据

    1:新建的web api项目 默认的访问api方式:  (get,post,delect,put)  api+控制器  以Post为例子 post提交单个参数: 接收方法  post提交多个参数  接 ...

  7. 测试基础-http协议(转)

    HTTP的特性 HTTP构建于TCP/IP协议之上,默认端口号是80 HTTP是无连接无状态的 HTTP报文 请求报文 HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规 ...

  8. Java Basic&Security Tools

    JDK Tools and Utilities Basic Tools These tools are the foundation of the JDK. They are the tools yo ...

  9. [T-ARA][Falling U]

    歌词来源:http://music.163.com/#/song?id=27506041 作词:韩尚元 [作词:韩尚元] 作曲:韩尚元 [作曲:韩尚元] Love is pain Love is pa ...

  10. Wampserver 修改根目录

    wampserver 默认根目录在 www 文件夹下 修改根目录方法如下: 1. 在打算存放项目或代码的位置新建文件夹(我建在了C:/MyProject) 2. 打开 httpd.conf 文件(该文 ...