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

(整理自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函数

时间: 2024-10-07 02:20:21

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

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

在构造函数中调用virtual函数时,base class构造期间virtual函数是不会下降到derived class层 如: class Transaction{ public: Transaction(); virtual void logTransaction() const = 0; }; Transaction::Transaction() { logTransaction(); } class BuyTransaction: public Transaction{ public:

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

  1.当在一个子类当中调用构造函数,其父类构造函数肯定先被调用.如果此时父类构造函数中有一个virtual函数,子类当中也有,肯定执行父类当中的virtual函数,而此时子类当中的成员变量并未被初始化,所以无法调用子类与之对应的函数.即为指向虚函数表的指针vptr没被初始化又怎么去调用派生类的virtual函数呢?析构函数也相同,派生类先于基类被析构,又如何去找派生类相应的虚函数? 2.做法:将子类的某个函数改为non-virtual,然后在子类构造函数中传递参数给父类函数.然后父类的构造函数

Effective C++ Item 9 绝不在构造和析构过程中调用virtual函数

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层) 示例: <pre name="code" class="cpp">#include <iostream> #include <string> using namespace std; c

effective c++ 条款09:绝不在构造和析构过程中调用virtual函数

记住:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class. class Transaction { //所有交易的base class public: Transaction(); virtual void logTransaction() const = 0; //做出一份因类型不同而不同的log }; Transaction::Transaction() { ... logTransaction(); } class BuyTransaction:

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

在base构造期间virtual函数不是virtual函数,因为构造函数首先从base构造函数开始执行,执行的时候继承类的对象并没有也就无从调用其成员函数,用以下这种方式能解决问题: 上面的做法,在要对继承类使用构造函数的时候通过参数传递给base构造函数. 注意:

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

在构造和析构执行期间不要调用virtual函数,因为这类调用从不会下降至derived class(比起当前执行构造函数和析构函数) 如果在base class 构造函数或者析构函数调用virtual,derived class构造时会先构造base class,则base class中的virtual实际调用是base class的; 第一种解释:derived class类先执行构造base class部分,然而在base class构造过程中,derived class部分是为初始化的,如

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

看过C++对象模型的话就可以知道,在构造基类时,完整的vtable没有建立起来(表项没有被相应的子类函数替换),因而无法调用到子类的函数(即构造函数中的virtual函数是本类里的方法,不是virtual的).书中也说即使调用了,因为构造函数的调用顺序,父类在构造时子类的成员还没有初始化可能,此时调用virtual函数无疑会造成混乱. 类似的关于析构过程,是一个子类先执行然后再父类执行的过程,如果在父类中调用virtual函数那么可能涉及到的子类数据已经被释放或者重置了,应该是出于这个考虑吧.

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

如下是一个股票交易的例子: 1 class Transaction // 交易的基类 2 { 3 public: 4 Transaction(); 5 virtual void logTransaction() const = 0; // 用于记录交易日志 6 }; 7 Transaction::Transaction() 8 { 9 logTransaction(); // 调用虚函数 10 } 11 12 class BuyTransaction : public Transaction /

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

下面是一个用来塑模股市交易的类: derived的类的构造函数被调用,但是首先得调用基类Transaction的构造函数,但是在后面还得调用virrual函数,这个时候子类的对象的构造还没有完成,那么虚函数也就没有意思,也就可以说虚函数现在还不是虚函数,在derived class对象构造期间,对象的类型还是base类的而不是derived class.不光是virtual函数会被解析为base calss,运行期间类型信息也会把对象视为base class.所以说现在根本就还没有derived