Reputation: 429
Below is the code. I don't understand why it behaves like this:
#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?
}
The last statement bar->Bar() prints "Foo()" which confused me. This comes from the following blog: https://shaharmike.com/cpp/vtable-part4/. Basically it relates to structure of the vtable of the class and how compiler handles the cast between two parent classes of a multi inherited class with virtual functions. Can anybody help me understand this?
Upvotes: 0
Views: 128
Reputation: 32732
When you write (BarInterface*)(foo);
, you're lying to the compiler. You're telling it that foo
is really a pointer to BarInterface
, so the compiler will believe you. Since it isn't, you get Undefined Behavior when you attempt to dereference the pointer. Discussing how a compiled program behaves in the presence of Undefined Behavior is often pointless.
In this case, the way your compiler has populated the vtables, the entry for FooInterface::Foo
appears to be in the same place as the entry for BarInterface::Bar
. As a result, when you call bar->Bar()
, the compiler looks into the FooInterface
vtable, finds the entry for FooInterface::Foo
, and calls that. If the class layouts would be different, or the function signatures different, far more serious consequences would ensue.
The solution, of course, is to use a dynamic_cast
which can perform the necessary sideways cast.
Upvotes: 2