【问题标题】:mixture of virtual and non-virtual inheritance虚拟和非虚拟继承的混合
【发布时间】:2012-04-15 21:34:18
【问题描述】:

在尝试更深入地分析 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. 现在,Left 中的g() 尝试调用虚方法f()。根据 C++ 规范,我们调用 f() 方法为指针的动态类型(即底部)

但是底部没有方法f()...至少不是唯一的方法。为什么这个程序执行Left::Base::f() 而不是Right::Base::f() 或者为什么它根本没有说明从底部调用f() 是不明确的?

【问题讨论】:

  • 当你将函数声明为虚拟时,它将一直是虚拟的
  • 这与手头的问题有什么关系?我知道我们需要调用动态类型的方法。除了方法也可以隐藏,你可以通过简单地用相同的签名重写它来隐藏虚拟方法,但我相信它是非虚拟的

标签: c++ inheritance multiple-inheritance virtual-functions


【解决方案1】:

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

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

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

【讨论】:

  • 这完全不是我问的... C++ 的规范说要调用动态类型的方法,但是在这里,动态类型根本没有 f()... 那我们为什么打电话吗?
  • @Bober02:动态类型实际上在层次结构的每一侧都有 两个 f() 一个。请注意,标准所说的是函数的 final overrider 将被调用,这是关键点。虽然有两个f,但Left::Base::f 有一个覆盖器
【解决方案2】:

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

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

【讨论】:

  • 感谢您的回答和评论。最终覆写者...该死的,这很复杂。是否有任何关于多重虚拟/非虚拟继承的好资料可以解释诸如此类的边缘情况?我找不到任何写得好的...
  • @Bober02 这并不复杂。想想存在的对象:因为继承不是虚拟的,所以你有两个 Base 子对象。一旦您能够识别对象,就很容易遵循覆盖链,唯一的奇怪行为在于单个方法可以覆盖多个虚拟函数(每个基数最多一个)跨度>
  • 一个方法可以覆盖多个虚函数是什么意思?你能举个例子吗?顺便说一句,在您看来,关于此类高级主题的最佳资源是什么?谢谢
  • @Bober02:最好的资源是经验,到处阅读。例如:struct A { virtual void f() {} }; struct B { virtual void f() {} }; struct C : A, B { virtual void f() {} }; C::fA::fB::f 的最终覆盖。签名必须匹配,但单个函数可以覆盖多个虚拟函数(根据示例)
  • 阅读无处不在...这是非常重要的声明。有什么关于高级主题的好书吗?顺便说一句,你介意看看我刚刚发布的这个问题吗?谢谢:stackoverflow.com/questions/10166926/…
猜你喜欢
  • 2016-05-20
  • 2016-10-26
  • 1970-01-01
  • 2011-12-30
  • 1970-01-01
  • 2012-09-27
  • 1970-01-01
  • 2013-08-24
相关资源
最近更新 更多