开始
有一个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的构造函数会在其之前被调用, 在这个构造函数中, logTransaction会被调用, 而问题在于这个时候被调用的并不是子类的函数, 而是基类的函数, 也就是说在构造函数的期间, virtual函数并不会下降到子类的函数,我们可以说:在base class的构造期间,virtual函数并不是virtual函数。
相同的道理同样作用于析构函数, 一旦子类的析构函数开始执行, 子类的成员变量就会进入未定义的的状态,可以说进入析构函数的子类对象就成为了一个基类对象。
侦测构造函数或者析构函数中是否调用了virtual函数,需要一定的手段, 如果Transaction有多个构造函数,每一个都需要执行相同的动作,那么避免代码重复的一个优秀做法是把共同的初始化代码放进一个初始化函数内:
class Transaction
{
public:
Transaction()
{
init();
}
virtual void logTransaction() const = 0;
...
private:
void init()
{
...
logTransaction();
}
};
这样的函数回逃避掉编译器的检查,在执行中会导致一个错误的产生,而如果logTransaction不是纯虚函数的话,运行会没有任何问题, 而检查错误行为会很难,所以我们要保证在构造函数和析构函数期间, 没有virtual被调用。
如果我们一定要在构造的过程中调用适当版本的logTransaction函数, 我们可以考虑将logTransaction改成非虚的函数, 然后子类需要传递必要的信息:
class Transaction
{
public:
explicit Transaction(const std::string logInfo);
void logTransaction(const std::string logInfo) const;
...
};
Transaction::Transaction(const std::string logInfo)
{
...
logTransaction(logInfo);
}
class BuyTransaction: public Transaction
{
public:
BuyTransaction(parameters)
: Transaction(createLogString (parameters))
{ ... }
...
private:
static std::string createLogString(parameters);
};
|