如下是一个股票交易的例子:

 class Transaction                               // 交易的基类
{
public:
Transaction();
virtual void logTransaction() const = ; // 用于记录交易日志
};
Transaction::Transaction()
{
logTransaction();              // 调用虚函数
} class BuyTransaction : public Transaction // 买进股票
{
virtual void logTransaction() const;
};
void BuyTransaction::logTransaction() const{ } class SellTransaction : public Transaction // 卖出股票
{
virtual void logTransaction() const;
};
void SellTransaction::logTransaction() const{ } int main()
{
BuyTransaction b; return ;
}

上述代码中执行BuyTransaction b时,会调用基类的构造函数,而基类的构造函数会调用一个虚函数来完成工作。但是该虚函数是否会调用派生类的对应函数呢?答案是否定的,解释如下:

1>  基类的构造是先于派生类的构造的。当基类的构造函数执行时,派生类中的成员尚未被初始化,如果允许基类构造期间调用派生类的函数,可能也会调用了没有初始化的“垃圾值”,导致不确定行为发生。

2>  基类构造期间,virtual函数的行为绝对不会伸到派生类中,因为此时构造的是基类,即此时构造的对象的类型是基类而非派生类,这点很重要。

但是有时我们在构造基类的时候又需要派生类中才有的参数信息,如何解决:

在基类中将logTransaction函数改为非虚函数,然后在构造派生类时将必要的参数传递到基类的构造函数中去,像如下这样:

 #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{ } class BuyTransaction : public Transaction // 买进股票
{
public:
BuyTransaction(const std::string& parameter) : Transaction(parameter){ }
}; int main()
{
BuyTransaction b("BT"); return ;
}

由上可知,如果基类构造时要使用派生类的信息,可以通过派生类构造函数的初始化列表传入相关参数,切不可调妄图调用虚函数实现,因为在构造基类期间其对象的类型是基类类型。

对于析构函数也是同样的道理,基类的析构总是在派生类析构后才进行,此时派生类的对象已经不存在了,而调用虚函数必然会引发一个不确定行为。当然你也可以将基类的虚函数实现后在析构函数中进行调用,但是这样的话,为什么不定义为非虚函数呢,虚函数的意义将不复存在了。

条款9:不要在构造和析构过程中调用virtual函数的更多相关文章

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

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

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

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

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

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

  4. 条款9:绝不在构造和析构过程中调用virtual函数(Never call virtual functions during construction or destruction)

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

  5. 条款09:绝不在构造和析构过程中调用virtual函数

    不该在构造函数和析构函数期间调用virtual函数,这一点是C++与jave/C#不同的地方之一. 假设有一个class继承体系,用来模拟股市交易如买进.卖出的订单等等.这样的交易一定要经过审计,所以 ...

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

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

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

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

  8. 【09】绝不在构造和析构过程中调用virtual方法

    1.绝不在构造和析构过程中调用virtual方法,为啥? 原因很简单,对于前者,这种情况下,子类专有成分还没有构造,对于后者,子类专有成分已经销毁,因此调用的并不是子类重写的方法,这不是程序员所期望的 ...

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

    下面是一个用来塑模股市交易的类: derived的类的构造函数被调用,但是首先得调用基类Transaction的构造函数,但是在后面还得调用virrual函数,这个时候子类的对象的构造还没有完成,那么 ...

随机推荐

  1. C#实现文件下载的几种方法

    //WriteFile实现下载 protected void Button2_Click(object sender, EventArgs e) { /* using System.IO; */ st ...

  2. 在集群环境中使用 EhCache 缓存系统|RMI 集群模式

    RMI 是 Java 的一种远程方法调用技术,是一种点对点的基于 Java 对象的通讯方式.EhCache 从 1.2 版本开始就支持 RMI 方式的缓存集群.在集群环境中 EhCache 所有缓存对 ...

  3. Chrome调试(debugger)总是进入paused in debugger状态

    在通过Chrome浏览器进行web前端开发时,我们会经常用到Chrome自带的debugger工具,但是经常按完快捷键(F12)后,页面会进入 paused in debugger状态,需要点击右上角 ...

  4. C# 16进制与字符串、字节数组之间的转换(转)

    1.请问c#中如何将十进制数的字符串转化成十六进制数的字符串   //十进制转二进制 Console.WriteLine("十进制166的二进制表示: "+Convert.ToSt ...

  5. JS常用的设计模式(12)—— 迭代器模式

    迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示. js中我们经常会封装一个each函数用来实现迭代器. array的迭代器: forEach = functio ...

  6. C++ builder 2010 操作Excel表格的编程实现

    //--------------------------------------------------------------------------- #include <vcl.h> ...

  7. Summarize code for the three presentation experiments

    Image Picker Controller @IBAction func experiment() { let controller = UIImagePickerController() sel ...

  8. 软件工程 speedsnail 冲刺8

    完成任务:蜗牛移动的界面调整,速度调整: 遇到问题: 问题1 进入关卡的开始蜗牛会跳一段距离 解决1 线程延迟3S: 明日任务: 划线实现挡板功能

  9. CentOS 5.X安装LAMP最高版本环境

    #------------CentOS 5.X安装LAMP最高版本环境------------------#! /bin/sh #安装Apacheyum install httpd -y#1.关闭se ...

  10. C#中sizeof的用法实例分析

    这篇文章主要介绍了C#中sizeof的用法,包括了常见的用法及注释事项,需要的朋友可以参考下.   sizeof是C#中非常重要的方法,本文就以实例形式分析C#中sizeof的用法.分享给大家供大家参 ...