条款09:绝不在构造和析构过程中调用virtual函数
假设有这样一个class继承体系,用来模拟股市交易如买进、卖出的订单等。这样的交易一定要经过审计,所以每当创建一个交易对象,在审计日志中也需要创建一笔适当记录。下面是一个看起来颇为合理的做法:
class Transaction {
public:
Transaction();
virtual void logTransaction() const = 0; //做出一份因类型不同而不同的日志
};
Transaction::Transaction() { //基类构造函数
...
logTransaction(); //最后动作是志记这笔交易
}
class Buy Transaction: public Transaction {
public:
virtual void logTransaction() const; //志记此类型交易
...
};
class SellTransaction: public Transaction {
public:
virtual void logTransaction() const;
...
};
现在执行以下程序:
BuyTrasaction b;
在创建对象的过程中,BuyTransaction构造函数被调用,但首先Transaction构造函数一定会更早被调用;而Trasaction构造函数的最后一行调用virtual函数logTransaction,问题就出现在这里。因为此时派生类BuyTransaction的成分还没有被构造,而基类构造期间virtual函数不会下降到派生类阶层,所以logTransaction只是Transaction内的版本,所以调用派生类的构造函数并没有像预期那样创建一个适当的副本日志。
解决上述问题的方案如下:
class Transaction {
public:
explicit Transaction(const string& logInfo);
void logTransaction(const string& logInfo) const; //non-virtual
...
};
Transaction::Transaction(const string& logInfo) {
...
logTransaction(logInfo);
}
class BuyTransaction: public Transaction {
public:
BuyTransaction(parameters): Transaction(parameters) { //将log信息传递给基类
...
}
}
请记住
- 在构造和析构期间不要调用virtual函数,因为这类调用从不下降至派生类。
条款10:令operator=返回一个reference to *this
赋值操作采用有结合律,如下所示:
x = y = z = 5;
x = (y = (z = 5)); //两者等价
为了实现类的“连锁赋值”,赋值操作符必须返回一个reference指向操作符的左侧实参。
class Widget {
public:
...
widget& operator=(const widget& rhs) { //返回reference指向当前对象
...
return* this; //返回左侧对象
}
...
};
请记住
- 令赋值操作符返回一个reference to *this。