(整理自Effctive C++,转载请注明。整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/

为方便采用书上的例子,先提出问题,在说解决方案。

1 问题

   1: class Transaction{

   2: public:

   3:     Transaction();

   4:     virtual void LogTransaction() const = 0 ;

   5:     ...

   6: };

   7:  

   8: Transaction::Transaction()                //Base class 的构造函数之实现

   9: {

  10:     ...

  11:     LogTransaction();                    //最后动作是调用LogTransaction函数

  12: }

  13:  

  14: class BuyTransaction:public Transaction{

  15: public:

  16:     virtual void LogTransaction() const ; 

  17:  

  18: };

假设在程序中:

   1: BuyTransaction b ;

BuyTransaction构造函数会被调用,但首先Transaction的构造函数会被调用;derived class对象内的base class成分会在derived class自身成分被构造之前先构造妥当。Transaction构造函数最后一行调用virtual函数LogTransaction是base class内的版本。是的,base class构造期间virtual函数绝对不会下降到derived class阶层。即“在base class 构造期间,virtual函数不是virtual函数”。

这一似乎反直觉的行为有个好理由:对于base class构造函数的执行更早于derived class构造函数,当base class构造函数执行时derived class的成员变量尚未初始化。如果此期间调用virtual函数下降至derived class阶层,要知道derived class的函数几乎必然取用local成员变量,而那些变量尚未初始化,C++不允许你这样做。

其实还有更根本的原因:在derived class对象的base class构造期间,对象的类型是derived class。

相同道理也适用于析构函数。一旦derived class析构函数开始执行,对象内的derived class 成员变量便呈现未定义的值。进入base class析构函数后对象就成为一个base class对象,而virtual函数等也那么看待它。

另外,侦测“构造函数或析构函数运行期间是否调用virtual函数”并不是很轻松。唯一能够避免此问题的做法就是:确定你的构造函数和析构函数都没有(在对象被创建和被销毁期间)调用virtual函数,而它们调用的所有函数也都服从同一约束。

2 解决方案

上面提到唯一能够避免此问题的做法就是:确定你的构造函数和析构函数都没有(在对象被创建和被销毁期间)调用virtual函数,而它们调用的所有函数也都服从同一约束。

但是,如何确保每次一继承体系上的对象被创建,都有适当版本的函数被调用呢?很显然,在base class构造函数内对着对象调用virtual函数是一种错误的做法。其他方案可以解决这个问题:一种做法是在class Transaction内将LogTransaction函数改为non-virtual,然后derived class构造函数传递必要的信息给Transaction构造函数,然后那个构造函数便可安全调用non-virtual LogTransaction:

   1: class Transaction{

   2: public:

   3:     explicit Transaction ( const string& logInfo ) ;

   4:     void LogTransaction ( const string& logInfo ) const ;//如今是个non-virtual函数

   5:     ...

   6: };

   7: Transaction::Transaction( const string& logInfo )

   8: {

   9:     LogTransaction(logInfo);                //如今是个non-virtual 调用

  10: }

  11:  

  12: class BuyTransaction:public Transaction{

  13: public:

  14:     BuyTransaction( parameters ):Transaction(createLogString(parameters))  //将log信息传递给base class构造函数

  15:     {...}

  16:     ...

  17: private:

  18:     static string createLogString(parameters) ;

  19: };

  20:  

换句话说由于无法使用virtual函数从base class向下调用,在构造函数期间,你可以借由“令derived class将必要的构造信息传递至base class构造函数”替换之而加以补偿。

令createLogString为static,也就不可能意外指向“初期未成熟之BuyTransaction对象尚未初始化的成员变量”。这很重要,正因为“那些成员变量处于未定义状态”,所以在“base class构造和析构期间调用的virtual函数不可下降至derived class” 。

请注意:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class。

Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. uVa 714 (二分法)

    Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu   Description   Before th ...

  2. iOS开发之视图控制器(UIViewController)

    视图控制器应该在MVC设计模式中扮演控制层(C)的角色,UIViewController的职责对内管理与之关联的View,对外跟其他UIViewController通信和协调.一个视图控制器管理一个视 ...

  3. BZOJ 3401: [Usaco2009 Mar]Look Up 仰望( 单调栈 )

    n <= 105 , 其实是10 ^ 5 ....坑...我一开始写了个模拟结果就 RE 了.. 发现这个后写了个单调栈就 A 了... ---------------------------- ...

  4. javascript学习(10)——[知识储备]链式调用

    上次我们简单的说了下单例的用法,这个也是在我们java中比较常见的设计模式. 今天简单说下链式调用,可能有很多人并没有听过链式调用,但是其实只要我简单的说下的话,你肯定基本上都在用,大家熟知的jQue ...

  5. Codeforces 486B - OR in Matrix

    矩阵的 OR ,也是醉了. 题目意思很简单,就是问你有没有这么一个矩阵,可以变化,得到输入的矩阵. 要求是这个矩阵每行都可以上下任意移动,每列都可以左右任意移动. 解题方法: 1.也是导致我WA 的原 ...

  6. OKR 方法 学习笔记

    最近公司兴起了对OKR这个词的讨论,并且听到时总会伴随提到KPI,提到绩效考核.那OKR到底是什么呢?与KPI的区别在哪里?与绩效考核有什么关系?它与我们现在推行的敏捷开发有啥关系呢?因此,就到网上查 ...

  7. HDU Good Numbers (热身赛2)

    转载请注明出处:http://blog.csdn.net/a1dark 分析:一道水题.找下规律就OK了.不过要注意特判一下0.因为0也是good number.这个把我坑惨了= =||| #incl ...

  8. 基于visual Studio2013解决算法导论之021单向循环链表

     题目 单向循环链表的操作 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <time.h> ...

  9. 基于visual Studio2013解决C语言竞赛题之0510求最大和

     题目

  10. MapReduce调度与执行原理之任务调度(续)

    前言 :本文旨在理清在Hadoop中一个MapReduce作业(Job)在提交到框架后的整个生命周期过程,权作总结和日后参考,如有问题,请不吝赐教.本文不涉及Hadoop的架构设计,如有兴趣请参考相关 ...