多态性如何涉及多重继承?
How polymorphism works involving multiple inheritance?
我现在正在研究与多重继承相关的主题。我想出了下面的代码,并不能完全弄清楚它背后的机制:
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
显然是一个错误。但是,method1
和 method2
中的函数调用让我感到困惑。它们也是虚函数调用,t
实际上是inheritMulti
类型。所以他们应该调用 vfunction
的 inheritMulti
版本,但是由于 inheritMulti
没有自己的版本,我不知道会发生什么。结果 method1
中的调用调用了 mid1
版本,而 method2
中的调用调用了 mid2
版本。它是未定义的并且只发生在我的编译器上吗?如果不是,为什么它会这样工作? vtable 是如何处理这种情况的呢?谢谢!
首先感谢您的帮助。我自己搜索了相关主题,所以是的,我知道 "diamond problem"。但我认为我的问题与此不同。我主要关心的是 method1
和 method2
中 "virtual function call" 的行为是否由标准明确定义或未定义。在编译器中,它的行为就像我上面提到的那样,但是标准承诺的行为是什么?如果定义明确,为什么会分别调用mid1
和'mid2'版本呢? (直观的想法是调用 inheritMulti
版本,因为 t
的类型实际上是 inheritMulti
)而且,大多数编译器如何处理这种情况?很奇怪 method1
和 method2
中的虚函数调用调用了不同的函数。再次感谢!
virtual关键字这里根本不玩
当两个 classes 具有同名函数,而第三个 class 继承自它们时,您需要手动指定 class 在调用该函数时引用哪个基础范围运算符 ::
.
void ambiguityMethod(){
mid1::vfunction();
}
或
void ambiguityMethod(){
mid2::vfunction();
}
或
void ambiguityMethod(){
root::vfunction();
}
with t :(警告:前面有奇怪的语法!)
t.mid1::vfunction();
您也可以重写此函数并调用一些基本 class 函数,因此只需指定一次您调用的是哪个函数:
void inheritMulti::vfunction() override {
return mid1::vfunction();
}
编译器将在调用 vfunction
时搜索最近的具有相同签名的函数,并调用该函数,而该函数又将调用 mid1
函数
您的代码描述了 "The diamond problem" 多重继承中的众所周知的问题(针对 vfunction
)。对于 method1
和 method2
一切正常,因为您正在从 this
创建 mid1
和 mid2
对象并从它们的名称空间调用 vfunction
。
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
将有两个名为 mid1
和 mid2
的子对象,每个子对象都维护自己的 vtable 指针。虽然你可能会认为当你这样做时
mid1& t1 = *this;
和mid2& t2 = *this;
t1 和 t2 都相同,但请尝试这样做...
bool same = ((void*)t1) == ((void*)t2); // Result false!
因为编译器会在后面生成一些代码来调整指针以指向正确的对象。
我现在正在研究与多重继承相关的主题。我想出了下面的代码,并不能完全弄清楚它背后的机制:
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
显然是一个错误。但是,method1
和 method2
中的函数调用让我感到困惑。它们也是虚函数调用,t
实际上是inheritMulti
类型。所以他们应该调用 vfunction
的 inheritMulti
版本,但是由于 inheritMulti
没有自己的版本,我不知道会发生什么。结果 method1
中的调用调用了 mid1
版本,而 method2
中的调用调用了 mid2
版本。它是未定义的并且只发生在我的编译器上吗?如果不是,为什么它会这样工作? vtable 是如何处理这种情况的呢?谢谢!
首先感谢您的帮助。我自己搜索了相关主题,所以是的,我知道 "diamond problem"。但我认为我的问题与此不同。我主要关心的是 method1
和 method2
中 "virtual function call" 的行为是否由标准明确定义或未定义。在编译器中,它的行为就像我上面提到的那样,但是标准承诺的行为是什么?如果定义明确,为什么会分别调用mid1
和'mid2'版本呢? (直观的想法是调用 inheritMulti
版本,因为 t
的类型实际上是 inheritMulti
)而且,大多数编译器如何处理这种情况?很奇怪 method1
和 method2
中的虚函数调用调用了不同的函数。再次感谢!
virtual关键字这里根本不玩
当两个 classes 具有同名函数,而第三个 class 继承自它们时,您需要手动指定 class 在调用该函数时引用哪个基础范围运算符 ::
.
void ambiguityMethod(){
mid1::vfunction();
}
或
void ambiguityMethod(){
mid2::vfunction();
}
或
void ambiguityMethod(){
root::vfunction();
}
with t :(警告:前面有奇怪的语法!)
t.mid1::vfunction();
您也可以重写此函数并调用一些基本 class 函数,因此只需指定一次您调用的是哪个函数:
void inheritMulti::vfunction() override {
return mid1::vfunction();
}
编译器将在调用 vfunction
时搜索最近的具有相同签名的函数,并调用该函数,而该函数又将调用 mid1
函数
您的代码描述了 "The diamond problem" 多重继承中的众所周知的问题(针对 vfunction
)。对于 method1
和 method2
一切正常,因为您正在从 this
创建 mid1
和 mid2
对象并从它们的名称空间调用 vfunction
。
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
将有两个名为 mid1
和 mid2
的子对象,每个子对象都维护自己的 vtable 指针。虽然你可能会认为当你这样做时
mid1& t1 = *this;
和mid2& t2 = *this;
t1 和 t2 都相同,但请尝试这样做...
bool same = ((void*)t1) == ((void*)t2); // Result false!
因为编译器会在后面生成一些代码来调整指针以指向正确的对象。