问题聚焦:

不要在构造函数和析构函数中调用virtual函数,因为这样的调用不会带来你预想的结果。

让我先来看一下在构造函数里调用一个virtual函数会发生什么结果
Demo


class Transaction {
public:
Transaction();
virtual void logTransaction() const = ;
};
Transaction::Transaction()
{
logTransaction();
}
//void Transaction::logTransaction() const //第一次编译时,注释掉该段代码
//{
// std::cout << "Transaction called" << std::endl;
//}
class BuyTransaction: public Transaction
{
public:
virtual void logTransaction() const;
};
void BuyTransaction::logTransaction() const
{
std::cout << "BuyTransaction called" << std::endl;
} //执行下面的语句会发生什么事情?
BuyTransaction b;

执行上述代码,会发现链接出错,报错:
表示没有Transaction::logTransaction() const的实现。
现在把上述代码中被注释的代码补上之后,再编译一次,OK,通过了。来看看结果是什么吧
 
不错,运行正常,也没有crash。不过,貌似不是我们想要的结果。
我们是对BuyTransaction类进行实例化,但是构造函数调用的是父类的构造函数,这是为什么呢?
下面我们来分析一下原因:
  1. 父类构造函数先调用,此时子类的局部成员变量还没准备好,这时如果将virtual函数下降至子类阶层,那么使用未初始化的部分可能会引起不明确的行为,所以C++直接禁止了这种行为,因此会出现上面的链接错误。
  2. 在子类的父类部分被构造期间,virtual函数并不认为是virtual函数,因为在初始化子类的父类部分时,编译器认为当前的对象是父类型,同时,直接认为子类部分是不存在的,因为子类成分并没有被初始化。
问题很明显了,就是在构造函数和析构函数中调用了virtual函数导致了上面的问题。那么有什么解决方案呢?
解决方案:令子类将必要的构造信息向上传递至base class构造函数
Demo

/** main.h **/
#include<iostream>
#include<string>
class Transaction {
public:
explicit Transaction(const std::string& logInfo);
void logTransaction(const std::string& logInfo) const; // 不再是虚函数
};
Transaction::Transaction(const std::string& logInfo)
{
logTransaction(logInfo);
}
void Transaction::logTransaction(const std::string& logInfo) const
{
std::string s = logInfo;
std::cout << s << std::endl;
}
class BuyTransaction: public Transaction
{
public:
BuyTransaction(const std::string& logInfo);
};
BuyTransaction::BuyTransaction(const std::string& logInfo):Transaction(logInfo) // 把信息传递给父类,通过父类打印出log
{
std::cout << "BuyTransaction called" << std::endl;
} /** main.cpp **/
#include<iostream>
#include"main.h"
int main()
{
BuyTransaction b("hello world");
system("Pause");
return ;
}

打印结果:
 
这是我们想要的结果。
小结:
在构造和析构函数内不要调用virtual函数,因为这类调用不下降至子类。
参考资料:
《Effective C++ 3rd》

Effective C++(9) 构造函数调用virtual函数会发生什么的更多相关文章

  1. C++构造函数调用虚函数的后果

    #include <iostream> class cx { public: virtual void func() { std::cout << "func&quo ...

  2. Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为方便采用书上的例子,先提出问题,在说解决方案. 1 问题 1: ...

  3. Effective C++ -----条款09:绝不在构造和析构过程中调用virtual函数

    在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).

  4. Effective C++ .09 不在构造和析构过程中调用virtual函数

    看过C++对象模型的话就可以知道,在构造基类时,完整的vtable没有建立起来(表项没有被相应的子类函数替换),因而无法调用到子类的函数(即构造函数中的virtual函数是本类里的方法,不是virtu ...

  5. Effective C++ 条款九、十 绝不在构造和析构过程中调用virtual函数|令operator=返回一个reference to *this

      1.当在一个子类当中调用构造函数,其父类构造函数肯定先被调用.如果此时父类构造函数中有一个virtual函数,子类当中也有,肯定执行父类当中的virtual函数,而此时子类当中的成员变量并未被初始 ...

  6. NO.8:绝不在构造或者析构过程中调用virtual函数

    在构造和析构执行期间不要调用virtual函数,因为这类调用从不会下降至derived class(比起当前执行构造函数和析构函数) 如果在base class 构造函数或者析构函数调用virtual ...

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

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

  8. [Effective JavaScript 笔记]第18条:理解函数调用、方法调用及构造函数调用之间的不同

    面向对象编程中,函数.方法.类的构造函数是三种不同的概念. JS中,它们只是单个构造对象的三种不同的使用模式. 三种不同的使用模式 函数调用 function hello(username){ ret ...

  9. 条款9:不要在构造和析构过程中调用virtual函数

    如下是一个股票交易的例子: class Transaction // 交易的基类 { public: Transaction(); ; // 用于记录交易日志 }; Transaction::Tran ...

随机推荐

  1. Cloudera Manager安装之Cloudera Manager安装前准备(CentOS6.5)(一)

    Cloudera Manager安装前准备 (一)机器准备 192.168.80.148   clouderamanager01 (部署ClouderaManager-server和Mirror se ...

  2. 多线程编程(三)-CountDownLatch的使用

    CountDownLatch的介绍 类CountDownLatch是同步功能得一个辅助类,使用效果就是给定一个计数,当使用CountDownLatch类的线程判断计数不为0时,则呈wait状态,如果是 ...

  3. WPF中设置了WindowStyle="None"后,窗口仍然有边框的解决方法

    1. 设置了窗体的WindowStyle="None",窗口还是右边框,如下图: 2. 这是因为窗体默认是可以改变大小的,所以需要修改ResizeMode的值 ResizeMode ...

  4. Objekt Orientierte Programmierung C++

    1.Funtion Overloading C++ erlaubt,dass einige Funktion gleiches Names deklariert wird.Der Formale Pa ...

  5. [PY3]——合并多个字典或映射(collections模块中的ChainMap 类)

    问题 现在有多个字典或者映射,你想将它们从逻辑上合并为一个单一的映射后执行某些操作, 比如查找值或者检查某些键是否存在. 解决方案 使用 collections 模块中的 ChainMap 类 Cha ...

  6. 汇编语言、与C语言、实现--汉诺塔--

    题意描述:   用汇编语言实现汉诺塔.只需要显示移盘次序,不必显示所移盘的大小,例如: X>Z,X>Y,Z>Y,X>Z,..... (n阶Hanoi塔问题)假设有三个分别命名为 ...

  7. JS常用时间处理方法

    这里会扩展一些JS常用时间处理方法,内置时间对象的方法不再赘述 -- 传送门:http://www.w3school.com.cn/js/jsref_obj_date.asp 时间格式化 -- 转换为 ...

  8. Python(1):入门

    安装: 在linux中一般都自带有python2.7的版本,如果想升级python到最新的版本可以参考其他博客(http://www.cnblogs.com/lanxuezaipiao/archive ...

  9. linux shell学习-1

    今天在使用$()这个命令的时候,如下,一直纳闷为何输出不是 "/usr": 一直在不断换着法子来试,原来是$()会将每个执行的命令单独隔开来的,及时是嵌套的命令,也会在执行逻辑上单 ...

  10. mklink /d 目录符号链接

    刚装好Windows Live Writer,却发现日志保存路径是默认的改都没法改,在C:\Users\用户名\Documents\My Weblog Posts下. 找了下,竟然可以用cmd的mkl ...