Effective C++ 条款9:绝不在构造和析构过程中调用virtual函数

本文探讨了在构造函数和析构函数中调用虚函数的问题,解释了为何在此情况下虚函数会失去多态性,并通过具体示例说明了这种情况下调用的是基类而非派生类的虚函数。

建议

  • 在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)

换句话说:在构造函数和析构函数中,virtual函数失去多态性。

试想一下,假设此时在构造函数和析构函数中,virtual函数没有失去多态性,会出现什么问题。我们知道构造次序吧,先构造base类部分,再构造derived部分。
那么在base类构造过程中,derived的参数都没有被初始化。virtual函数一旦具有多态性,调用的就是派生类的函数,那么它使用的派生类中数据成员将是未初始化的,必然导致未定义行为。
所以,一句话。在构造函数和析构函数中,virtual函数和普通函数没任何区别。

举个例子:

class Transaction{
public:
    Transaction();

    virtual void logTransaction()const//virtual function
    {
        //log the Transaction
        std::cout<<"This is Transaction logTransaction"<<std::endl;
    }
};
Transaction::Transaction()
{
    logTransaction();//called in Ctor
}

class BuyTransaction:public Transaction{
public:
    virtual void logTransaction()const
    {
        std::cout<<"This is BuyTransaction logTransaction"<<std::endl;
    }
};

class SellTransaction:public Transaction{
public:
    virtual void logTransaction()const
    {
        std::cout<<"This is SellTransaction logTransaction"<<std::endl;
    }
};

BuyTransaction b;

输出结果是:This is Transaction logTransaction
不是想要的This is BuyTransaction logTransaction。

在构造BuyTransaction对象b的时候,首先调用BuyTransaction类的构造函数(构造函数执行顺序请看这里:【关于基类和派生类的构造函数和析构函数的执行顺序问题】),在这个构造函数中,再调用基类Transaction的构造函数,在这里调用了logTransaction()函数。这样就输出了所显示的内容。在基类构造期间,不会下降到派生类去调用派生类的虚函数。
base class的构造函数先于derived class的构造函数。在base class构造函数期间,derived class的对象还没有构建,如果derived class的virtual用到了local变量,这时如果真的调用了derived class的virtual函数,会使用为初始化的变量,会有不明确的行为。所以C++不让你走这条路。
还有一个理由就是,在base class构造期间,对象类型是base class,不是derived class。virtual函数会被编译器解析(resolve to)base class。如果使用了运行期类型信息(runtime type information),编译器也会把它视为base class类型。

相同的道理同样适用于析构函数。析构过程和构造过程相反。先析构派生类部分,再析构基类部分。析构到基类时,派生类中的变量就是为初始化的,对象类型是基类类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值