条款9:绝不要在构造和析构中调用虚函数!
假设有个class继承体系,用来模拟股市交易。有个审计的方法。如下:
class Transaction //所有交易的基抽象类
{
public:
Transaction()
{
logTranscation();
}
virtual void logTranscation() const {
//....
}; //做出一份因为类型不同的日志记录
};
class BuyTransaction : public Transaction //买进交易类
{
public:
virtual void logTranscation() const {} //重写打印日志方法
};
那么如果,我们现在定义一个:
BuyTransaction b;
上述会发生什么呢?是否会如我们想的一样实现所谓的多态呢?
下面我们分析以下上述的执行过程:
BuyTransaction被定义一个的时候,肯定它的构造会被调用,但构造他之前肯定先调用他基类的构造函数,于是调用基类的logTranscation方法!!!那么我们思考这个方法是执行的基类的还是派生类重写的呢?
答案是很显然的:执行的是基类的方法!!!
最简单的解释就是:在派生类的基类构造期间,virtual函数绝不会下放到派生类的层次,因为派生类还未"出生呢" ,编译器绝不允许这种行为发生!
但是倘若要深层次的解释一下,也可以这样解释一下:在派生类的基类构造期间,对象真正的类型就是基类而不是派生类,这是由运行期类型信息RTTI来决定的!
**那么我们如何避免上述容易让人误解的情形呢?
答: 确定构造函数和析构函数没有调用任何virtual函数,并且它们调用的所有函数也同样符合同一约束!
但是我们如何又想要实现这样的类似功能: 确保每次在基类继承体系上的对象被创建的时候,就会又适当版本的方法被调用呢??下面给出一种方案:
class Transaction //基类
{
public:
Transaction(const std::string& loginfo) //基类构造的参数让子类传入
{
logTranscation(loginfo);
}
virtual void logTranscation(const std::string& loginfo) const = 0;
};
class BuyTransaction : public Transaction
{
public:
//根据参数不同生成字符串不同
std::string createLogString(int parameter);
//调用父类构造传入参数
BuyTransaction(int parameter): Transaction(createLogString(parameter))
{ //...}
};
我们的思想就是:
虽然我们无法将virtual函数从基类向下派送,但是可以令派生类将必要的构造信息向上传递到基类的构造进行弥补!!!
总结: 基类的构造和析构函数中切记不要调用任何virtual或者包含virtual的函数,因为这类调用无法下降的派生类。
结尾: 我是航行的小土豆,喜欢我的程序猿朋友们,欢迎点赞+关注哦!希望大家多多支持我哦!有相关不懂问题,可以留言一起探讨哦!
如有引用或转载记得标注哦!