【问题标题】:How polymorphism works involving multiple inheritance?涉及多重继承的多态性如何工作?
【发布时间】:2015-10-10 00:04:52
【问题描述】:

我现在正在研究与多重继承相关的主题。我想出了以下代码,但无法完全弄清楚其背后的机制:

struct root
{
    virtual void vfunction(){ /* root version */ }
};

struct mid1:public root
{
    virtual void vfunction(){ /* mid1 version */ }
};

struct mid2:public root
{
    virtual void vfunction(){ /* mid2 version */ }
};

struct inheritMulti:public mid1, public mid2
{
    void ambiguityMethod(){
        vfunction();    // error: ambiguous
    }
    void method1(){
        mid1& t = *this;
        t.vfunction();
    }
    void method2(){
        mid2& t = *this;
        t.vfunction();
    }
};

ambiguityMethod 显然是一个错误。但是,method1method2 中的函数调用让我感到困惑。它们也是虚函数调用,t 实际上是inheritMulti 类型。所以他们应该调用vfunctioninheritMulti版本,但是由于inheritMulti没有自己的版本,我不知道会发生什么。事实证明,method1 中的调用调用了mid1 版本,而method2 中的调用调用了mid2 版本。它是未定义的东西,只发生在我的编译器上吗?如果不是,为什么它会这样工作? vtable 如何处理这种情况?谢谢!


首先感谢您的帮助。我自己搜索过相关主题,所以是的,我知道“钻石问题”。但我认为我的问题与此不同。我主要关心的是method1method2 中“虚拟函数调用”的行为是由标准明确定义还是未定义。在编译器中,它的行为就像我上面提到的那样,但是标准所承诺的行为吗?如果定义明确,为什么分别调用mid1和'mid2'版本? (直观的想法是调用inheritMulti 版本,因为t 的类型实际上是inheritMulti)而且,大多数编译器如何处理这种情况?奇怪的是,method1method2 中的虚函数调用调用了不同的函数。再次感谢!

【问题讨论】:

  • 你认为这里应该发生什么?

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


【解决方案1】:
void method1(){
    mid1& t = *this;
    t.vfunction();
}

这里你调用的是vfunction,它没有在inheritMulti中定义,因此它将搜索vfunction的最接近的定义,并且存在于mid1中。查看层次结构。

Root->Mid1->inheritMulti

Root->Mid2->inheritMulti

的情况下相同
`void method2(){
    mid2& t = *this;
    t.vfunction();
}

在 mid2 中也找到了最接近的定义,因此是输出。这些调用没有歧义,因为两个结构都在那里创建了自己的vfunction 副本。

inheritMulti 将有两个名为 mid1mid2 的子对象,每个子对象都维护自己的 vtable 指针。虽然你可能会认为当你这样做时

mid1& t1 = *this;

mid2& t2 = *this;

t1 和 t2 都相同,但请尝试这样做...

bool same = ((void*)t1) == ((void*)t2);  // Result false!

因为编译器会在后面生成一些代码来调整指针以指向正确的对象。

【讨论】:

  • 谢谢!但还有一个问题。如果像这样涉及多重继承,vtable 是如何工作的?
【解决方案2】:

virtual 关键字在这里根本不起作用。

当两个类具有相同名称的函数,并且第三个类继承自它们两者时,您需要手动指定使用范围运算符 :: 调用该函数时引用的基类。

 void ambiguityMethod(){
        mid1::vfunction();
    }

 void ambiguityMethod(){
        mid2::vfunction();
    }

 void ambiguityMethod(){
        root::vfunction();
    }

with t :(警告:前面有奇怪的语法!)
t.mid1::vfunction();

你也可以重写这个函数并调用一些基类函数,从而只指定一次你调用哪个函数:

void inheritMulti::vfunction() override {
  return mid1::vfunction();
}

编译器将在调用vfunction时搜索最近的具有相同签名的函数,并调用该函数,而该函数又将调用mid1函数

【讨论】:

    【解决方案3】:

    您的代码描述了多重继承中众所周知的“钻石问题”问题(针对vfunction)。对于method1method2,一切正常,因为您正在从this 创建mid1mid2 对象,并从它们的命名空间调用vfunction

    【讨论】:

    • 不是真正的钻石,也不是问题。每当您有 MI 甚至多个 using namespace 或当您有多个导入时使用带有“import”的语言时,您都可能会模棱两可。
    猜你喜欢
    • 1970-01-01
    • 2021-10-30
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    相关资源
    最近更新 更多