C++ 在具有虚拟函数的多继承类的两个父类之间进行强制转换会导致奇怪的行为
下面是代码。我不明白它为什么会这样:C++ 在具有虚拟函数的多继承类的两个父类之间进行强制转换会导致奇怪的行为,c++,multiple-inheritance,virtual-functions,vtable,C++,Multiple Inheritance,Virtual Functions,Vtable,下面是代码。我不明白它为什么会这样: #include <iostream> using namespace std; class FooInterface { public: virtual ~FooInterface() = default; virtual void Foo() = 0; }; class BarInterface { public: virtual ~BarInterface() = default; virtual vo
#include <iostream>
using namespace std;
class FooInterface {
public:
virtual ~FooInterface() = default;
virtual void Foo() = 0;
};
class BarInterface {
public:
virtual ~BarInterface() = default;
virtual void Bar() = 0;
};
class Concrete : public FooInterface, public BarInterface {
public:
void Foo() override { cout << "Foo()" << endl; }
void Bar() override { cout << "Bar()" << endl; }
};
int main() {
Concrete c;
c.Foo();
c.Bar();
FooInterface* foo = &c;
foo->Foo();
BarInterface* bar = (BarInterface*)(foo);
bar->Bar(); // Prints "Foo()" - WTF?
}
#包括
使用名称空间std;
类接口{
公众:
virtual~FooInterface()=默认值;
虚拟void Foo()=0;
};
类界面{
公众:
virtual~BarInterface()=默认值;
虚拟空心条()=0;
};
类具体:公共接口、公共BarInterface{
公众:
void Foo()在编写(BarInterface*)(Foo)时重写{cout;
,你在对编译器撒谎。你告诉它foo
实际上是一个指向BarInterface
的指针,因此编译器会相信你。因为它不是,所以当你试图取消引用指针时,你会得到未定义的行为。讨论一个已编译程序在存在未定义行为时的行为通常是有意义的少一点
在本例中,按照编译器填充vtables的方式,FooInterface::Foo
的条目似乎与BarInterface::Bar
的条目位于同一位置。因此,当调用Bar->Bar()时
,编译器查看FooInterface
vtable,找到FooInterface::Foo
的条目,并调用该条目。如果类布局不同,或者函数签名不同,则会导致更严重的后果
当然,解决方案是使用dynamic_cast
,它可以执行必要的横向转换。当您编写(BarInterface*)(foo);
,你在对编译器撒谎。你告诉它foo
实际上是一个指向BarInterface
的指针,因此编译器会相信你。因为它不是,所以当你试图取消引用指针时,你会得到未定义的行为。讨论一个已编译程序在存在未定义行为时的行为通常是有意义的少一点
在本例中,按照编译器填充vtables的方式,FooInterface::Foo
的条目似乎与BarInterface::Bar
的条目位于同一位置。因此,当调用Bar->Bar()时
,编译器查看FooInterface
vtable,找到FooInterface::Foo
的条目,并调用该条目。如果类布局不同,或者函数签名不同,则会导致更严重的后果
当然,解决方案是使用dynamic_cast
,它可以执行必要的侧向转换。BarInterface*bar=dynamic_cast(foo);@MinorThreat BarInterface*bar=dynamic_cast(foo);打印“bar()”,但我的代码打印“foo()”。因此,C类型强制转换没有执行动态强制转换。您的问题需要对对象的动态类型进行运行时分析。C样式强制转换在此处不起作用,因为它只是关闭类型检查并返回指向FooInterface子对象的同一指针。BarInterface*bar=dynamic_cast(foo);@MinorThreat BarInterface*bar=dynamic_cast(foo);打印“Bar()”但我的代码打印“Foo()”。因此C类型转换没有执行动态转换。您的问题需要对对象的动态类型进行运行时分析。C样式转换在这里不起作用,因为它只是关闭类型检查并返回指向FooInterface子对象的同一指针。转换不是(总是)未定义的行为。假设BarInterface
的对齐要求不比FooInterface
严格,则允许强制转换(否则为未定义的行为,但考虑到类型基本相同,情况可能并非如此)。生成的BarInterface*
存在,但实际上并不指向BarInterface
对象。将其返回到FooInterface*
将成功检索原始文件,并且尝试在未定义的->
中取消对其的引用。@HTNW很好的一点。我没有考虑UB的确切时间开始。强制转换不是(始终)未定义的行为。假设BarInterface
的对齐要求不比FooInterface
严格,则允许强制转换(否则它是未定义的行为,但考虑到类型基本相同,情况可能并非如此)。生成的BarInterface*
存在,但实际上并不指向BarInterface
对象。将其返回到FooInterface*
将成功检索原始文件,并且尝试在未定义的->
中取消对其的引用。@HTNW很好的一点。我没有考虑UB的确切时间踢进来。