条款09: 绝不在构造和析构过程中调用 virtual 函数
Never call virtual functions during construction or destruction
不应该在构造函数和析构函数期间调用 virtual 函数,因为这样的调用不会带来预想的结果.
假设有个 class 继承体系,用来模塑股市交易如买进,卖出的订单等等.这样的交易一定要经过审计,所以每当创建一个交易对象,在审计日志中也需要创建一笔适当记录.下面是一个看起来颇为合理的做法:
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构造函数一定会更早地被调用.derived class 对象内的base class 成分会在derived class 自身成分在构造之前先构造,Transaction构造函数的最后一行调用 virtual 函数logTransaction,这正是引发惊奇的起点.这时候被调用的logTransaction是Transaction内的版本,不是BuyTransaction内的版本,即使目前即将建立的对象类型是BuyTransaction.base
class 构造期间 virtual 函数绝不会下降到derived class 阶层.非正式的说话比较传神:在base class 构造期间,virtual 函数不是 virtual 函数.
根本的原因是:在derived class 对象的base class 构造期间,对象的类型是base class 而不是derived class.不只是 virtual 函数会被编译器解析至base class.若使用运行期类型信息(如 dynamic_cast 和 typeid)也会把对象视为base class 类型。对象在derived class 构造函数开始执行前不会成为一个derived
class 对象.
相同道理也适用于析构函数.一旦derived class 析构函数开始执行,对象内derived class 成员变量便开始呈现未定义值,所以C++视它们仿佛不存在.进入base class 析构函数后对象就成为一个base class 对象,而C++的任何部分包括 virtual 函数,dynamic_cast 等等也那么看待它.
一个好的做法就是:确定构造函数和析构函数都没有(在对象被创建和被销毁期间)调用 virtual 函数,而它们调用的所有函数都服从同一约束.
注意:
在构造和析构期间不要调用 virtual 函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).
版权声明:本文为博主原创文章,未经博主允许不得转载。