下面是代码。我不明白为什么会这样:
#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?
}
最后一条语句bar-> Bar()打印“ Foo()”,这让我感到困惑。这来自以下博客:https://shaharmike.com/cpp/vtable-part4/。基本上,它涉及类的vtable的结构,以及编译器如何处理具有虚拟功能的多继承类的两个父类之间的转换。有人可以帮我理解吗?
[编写(BarInterface*)(foo);
时,您在对编译器撒谎。您告诉它foo
实际上是指向BarInterface
的指针,因此编译器会相信您。由于不是这样,因此在尝试取消引用指针时会出现“未定义行为”。讨论存在未定义行为的情况下已编译程序的行为通常毫无意义。
在这种情况下,即编译器填充vtable的方式,FooInterface::Foo
的条目似乎与BarInterface::Bar
的条目位于同一位置。结果,当您调用bar->Bar()
时,编译器将查找FooInterface
vtable,找到FooInterface::Foo
的条目,然后对其进行调用。如果类的布局不同,或者函数的签名不同,那么后果将更加严重。
解决方案,当然是使用可以执行必要的横向投射的dynamic_cast
。