C++构造函数和析构函数为什么不能调用虚函数?

构造函数里面包含virtual函数的情况

先看一段代码

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; //怎样实现这种类型交易的登录?  

};

这段代码来自《Effecitive C++》条款09,当声明一个BuyTransaction对象的时候,首先Transaction的构造函数会被调用,从而其virtual函数也被调动,这里就是引发惊奇的起点。这时候被调用的logTransaction是Transaction的版本,而不是派生类BuyTransaction的版本。

我们再看一段代码。

class Base
{
public:
    Base()
    {
        Fuction();
    }

    virtual void Fuction()
    {
        cout << "Base::Fuction" << endl;
    }
};

class A : public Base
{
public:
    A()
    {
        Fuction();
    }

    virtual void Fuction()
    {
        cout << "A::Fuction" << endl;
    }
};

当声明一个派生类对象,会发生什么呢?

A a;

程序输出为

Base::Fuction
A::Fuction

A类继承自Base,在Base中有虚函数Function,并且有代码,同时构造函数调用了虚函数,此时对象a先调用基类Base的构造函数,从而调用了Function函数,此时调用的还是基类的Functio函数,所以并没有实现多态的功能。

总结

在基类的构造过程中,虚函数调用从不会被传递到派生类中。派生类对象表现出来的行为好象其本身就是基类型。不规范地说,在基类的构造过程中,虚函数并没有被”构造”。也就是说,在派生类对象的基类子对象构造期间,调用的虚函数的版本是基类的而不是子类的。

对上面这种看上去有点违背直觉的行为可以用一个理由来解释-因 为基类构造器是在派生类之前执行的,所以在基类构造器运行的时候派生类的数据成员还没有被初始化。如果在基类的构造过程中对虚函数的调用传递到了派生类, 派生类对象当然可以参照引用局部的数据成员,但是这些数据成员其时尚未被初始化。这将会导致无休止的未定义行为和彻夜的代码调试。沿类层次往下调用尚未初始化的对象的某些部分本来就是危险的,所以C++干脆不让你这样做。

在对象的析构期间,存在与上面同样的逻辑。一旦一个派生类的析构器运行起来,该对象的派生类数据成员就被假设为是未定义的值,这样以来,C++ 就把它们当做是不存在一样。一旦进入到基类的析构器中,该对象即变为一个基类对象,C++中各个部分(虚函数,dynamic_cast运算符等等)都这样处理。

猜你喜欢

转载自blog.csdn.net/weixin_38169413/article/details/81587853