class Transaction {// 所有交易的基类 public: Transaction(); virtual void logTransaction() const = 0;//建立依赖于具体交易类型的登录项 ... }; 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++干脆不让你这样做。