欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

不要在构造和析构函数中调用虚函数

程序员文章站 2024-03-21 11:01:22
...

注:本文对应Effective C++ 条款9


基类是一个模拟股票交易的类,成员函数logTransaction()是记录每一笔交易,具体买/卖派生类实现自己的记录函数。

基类的构造函数中调用了这个虚函数。
先来思考下面这段代码:

#include<iostream>
using namespace std;

//交易类
class Transaction
{
public:
    Transaction()
    {
        logTransaction();
    }

    //记录每一笔买/卖记录
    virtual void logTransaction() const = 0;
};

//买进类
class BuyTransaction :public Transaction
{
public:
    virtual void logTransaction()const
    {
        cout << "BuyTransaction::logTransaction()" << endl;
    }
};

//卖出类
class SellTransaction :public Transaction
{
public:
    virtual void logTransaction()const
    {
        cout << "SellTransaction::logTransaction()" << endl;
    }
};

int main()
{
    BuyTransaction b;
    return 0;
}

下面是VS下的运行实例:
不要在构造和析构函数中调用虚函数

下面是linux下的运行实例:
不要在构造和析构函数中调用虚函数

都是直接被编译器拒绝了。


思考为什么?有什么错误吗?

当构造派生类对象构造时,先调用基类的构造函数,而在基类的构造函数中,调用了虚函数,这时候调用的是基类的logTransaction()函数,并不是派生类自己实现的logTransaction()函数。
换句话说,即使当前要建立的对象是派生类对象,但是基类构造函数期间,虚函数不会下降到派生类的层面,因为基类构造期间虚函数不是虚函数。

换种容易理解的方式,基类构造函数先于派生类构造函数执行,当基类构造函数执行时,派生类的成员变量尚未初始化,如果此时调用虚函数,并且下降至派生类层面,因为派生类的函数必然取用了自己的成员变量,但是此时它的成员变量还没有被初始化,这是一个通向未知错误的道路,所以C++阻止了你走这条路。

析构函数亦然,一旦派生类的析构函数开始执行,对象内的派生类成员就是未定义的,如果这时再进入基类的析构函数中,调用的虚函数必然是基类的,因为派生类已经不是一个完整的类型了。

综上所述,请不要在构造和析构函数中调用虚函数,大多数的编译器也都阻止了这个行为。