美文网首页
Effectivec++学习笔记(item9)

Effectivec++学习笔记(item9)

作者: 懒生活 | 来源:发表于2021-09-30 19:11 被阅读0次

item9: Never call virtual function during construction or destruction
如果要简单的总结,这个章节几句话就能说清楚重点,在构造函数和析构函数中不能调用虚函数。因为在构造的时候,是会递归调用父类的构造函数,如果父类的构造函数中,会调用虚函数,那么按我们对虚函数的理解这个虚函数会通过vbpr最终调用该继承类的函数实现,但此时正处于初始化阶段,继承类的分量还没有开始初始化,这样子调用就会引发异常。析构函数也是递归调用的,他先清理的是继承类的成分,然后去清理父类的成分,清理父类的时候,继承类成分已经不存在,这时候父类的析构函数如果还用虚函数,那引发类似的异常。
到此作者这个章节的关键点已经描述清楚了。但是大神的著作不能这样草草消化。这个章节Scott用了近4页的篇幅,还有其他附带的值得好好思考。

  1. 为什么Scott说这个地方c++和java/c#的处理方式有差异
    2)如果在构造的时候,我们不能通过父类的虚函数来做一些继承类特定的工作,那么我们可以用怎样的安全方式实现上述的目的?

引申: 为什么在构造函数\析构函数中调用virtual函数c++与java不一样

实际上java和c#允许在构造函数或者析构函数中调用virtual函数,而且virtual机制也发挥了作用,实际调用的是继承类的实现。不像c++,允许且编译成功了,但运行时发现调用的是父类自己的实现。java和c#中虽然virtual机制生效了,但是和c++遭遇的问题本质是一样的,虽然可以调用,但所有的继承类成员还没有初始化,此时这个函数如果有用到数据就可能有异常,或者用到的数据不是你期望的值。

引申:设计父类的时候如何限定每个继承类创建的时候都会有一个"约定的动作"

比如有这样的设计场景,我希望设计一个父类,所有继承类当创建时会自动有个继承类信息的log打印,并且不允许没有log打印的继承类创建。一种错误做法是在父类的构造函数中调用virtual 打印函数,这个打印函数具体打印内容在继承类是必须定义(因为父类里是virtual,继承类如果没有创建会告警)。这样子我们以为每次继承类创建的时候都会通过父类回调virtual打印函数的具体实现来,但实际上,因为父类在调用该virtual打印函数的时候,继承类还没有初始化,这样子是不允许的。
为了达到上述的目的,Scott推荐的技巧是:
定义父类的时候,声明一个带入参的构造函数,屏蔽默认构造函数。要求继承类在初始化,触发父类构造的时候,为父类的构造函数传入不同继承类不同要求的入参(可能是不同的打印语句)

class Transaction
{
                explicit Transaction(const string& logInfo);
                void keepInitLog(logInfo);
}
Transaction::Transaction(const string& logInfo)
{
                keepInitLog(logInfo);
}
class BuyTrans : public Transaction
{
                public:
                BuyTrans(string mylog)
                :Transaction(mylog)
                { ... }
}

相关文章

网友评论

      本文标题:Effectivec++学习笔记(item9)

      本文链接:https://www.haomeiwen.com/subject/bvnenltx.html