多态如何工作涉及多重继承?

时间:2015-07-20 18:27:22

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

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

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类型。因此,他们应该调用inheritMultivfunction,但由于inheritMulti没有自己的版本,我不知道会发生什么。结果是method1中的来电调用了mid1版本,而method2中的来电则调用mid2版本。它是不确定的东西,只发生在我的编译器上?如果没有,为什么它会像这样工作? vtable如何处理这种情况?谢谢!

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

3 个答案:

答案 0 :(得分:1)

虚拟关键字根本不在这里播放。

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

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

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

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

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

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

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

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

答案 1 :(得分:1)

您的代码正在描述"钻石问题"多重继承中众所周知的问题(对于vfunction)。对于method1method2,一切正常,因为您正在从mid1创建mid2this个对象,并从其名称空间调用vfunction

答案 2 :(得分:1)

void method1(){
    mid1& t = *this;
    t.vfunction();
}

您在vfunction调用inheritMulti未在vfunction中定义,因此会搜索mid1的最近定义,并且`void method2(){ mid2& t = *this; t.vfunction(); } 中存在该定义。查看层次结构。

根 - > Mid1-> inheritMulti

根 - > Mid2-> inheritMulti

的情况相同

vfunction

这里也是最接近的定义在mid2中找到,因此输出。这些调用不明确,因为两个结构都创建了自己的inheritMulti

副本

mid1将有两个名为mid2mid1& t1 = *this; 的子对象,每个子对象都维护自己的vtable指针。虽然你可能会认为当你这样做时

mid2& t2 = *this;

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

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

public class UUIDStringCustomType extends AbstractSingleColumnStandardBasicType {

    public UUIDStringCustomType() {
        super(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE);
    }

    @Override
    public String getName() {
        return "pg-uuid";
    }

}

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