虚拟和非虚拟继承的混合

mixture of virtual and non-virtual inheritance

本文关键字:虚拟 混合 继承      更新时间:2023-10-16

在试图更深入地分析C++的继承机制时,我偶然发现了以下示例:

#include<iostream>
using namespace std;
class Base {
public:
    virtual void f(){
    cout << "Base.f" << endl; 
    }
};
class Left : public Base { //NOT VIRTUAL!!!
public:
void g(){ 
        f();
    }     
};
class Right : public Base{
public:
    virtual void f(){
    cout << "Right.f" << endl; 
    }
};
class Bottom : public Left, public Right{
public:
    Bottom(int arg){ }
    //void f() { }
};
int main(int argc,char **argv)
{
    Bottom* b = new Bottom(23);
    b->g();
}

很明显,打电话

b->f()

是模棱两可的,因此在对象底部没有唯一的方法f()。现在,打电话

b->g()

工作正常并打印

Base.f

好吧,据我所知:

  1. 静态类型是 Bottom,所以我们称它为 g() 方法,因为它是非虚拟的
  2. g()方法是从 Left 继承的,所以我们称之为继承的方法
  3. 现在,左边的g()尝试调用虚拟方法f()。根据C++分离,我们调用f()指针的动态类型的方法(即 Bottom)

但是底部没有方法f()...至少不是独一无二的。为什么这个程序执行Left::Base::f()而不是Right::Base::f(),或者为什么它根本不声明对f()的调用从底部是模棱两可的?

简短

的回答是(如您所指出的),Bottom没有方法f(),因此无需尝试调用它。

Bottom包含两个子对象LeftRight。它们中的每一个都继承自Base,所以Bottom包含成员函数Left::f()Right::f(),但没有Bottom::f()。由于 Bottom 不会覆盖Left::f()(例如使用 Right::f()),因此 Base::f()Left::g() 中唯一的最终覆盖器。

参见 C++03 标准 10.3.9 中的示例。

由于没有虚拟继承,因此Bottom对象中有两个Base对象的副本。但是,如果将层次结构向上移动到定义g() Left,则只有一个Base子对象,这就是被调用的对象。由于它没有在Left(或Bottom)中被覆盖,它将调用Base版本。请注意,Right::f()仅覆盖其自己的Base子对象的f()

直接在Bottom上调用f是模棱两可的,因为没有f() Bottom它会尝试查看其基础并会找到Left::Base::fRight::Base::f,编译器不知道使用两者中的哪一个。