2

I understand the concepts of vptr and vtable and their role in implementing this mechanism.

The compiler constructs a vtable for each class with at least one virtual function and adds a vptr pointer to each class instance so that it can access the vtable and execute the correct function in runtime, but I fail to understand how exactly.

Consider the following code:

struct A
{
  virtual void foo() { cout << "A::foo" << endl; }
  virtual void goo() { cout << "A::goo" << endl; }
};

struct B
{
  virtual void goo() { cout << "B::goo" << endl; }
  virtual void foo() { cout << "B::foo" << endl; }
};

int main()
{
  A *r = (A *)(new B());
  r->foo();
}

output:

B::goo

Could you explain how and why B::goo was executed? did the compiler determine the place to look for inside the vtable in compile time? Thanks.

EDIT:

The above code snippet is just an example that I thought would make you understand which part of the mechanism I don't understand - clearly, I was wrong :)

Please look at the following example:

struct A
{
  virtual void foo() {/* some implementation */}
  virtual void goo() {/* some implementation */}
};

int main()
{
  A *a = new A();
  a->foo(); // <=====
}

For this example the compiler constructs a vtable with two function pointers inside, one points to foo, the other to goo. When calling foo, what happens exactly? how does the compiler know which of the pointers(inside vtable) to use? hope this makes it clear.

katkato
  • 149
  • 5
  • 1
    `reinterpret_cast` leads often to UB. – Jarod42 Jun 01 '21 at 13:24
  • Crashes on my machine when it tries to do `r->foo();` That's the great thing about **undefined behavior**, the result that you're seeing are just as good as the result I'm seeing. – Eljay Jun 01 '21 at 13:48
  • Does this answer your question? [How are virtual functions and vtable implemented?](https://stackoverflow.com/questions/99297/how-are-virtual-functions-and-vtable-implemented) – Richard Critten Jun 01 '21 at 14:05

1 Answers1

7

Your example has undefined behaviour: you are violating the strict aliasing rule when trying to alias the B object through an A pointer.

As always with undefined behaviour, all bets are off, and any further analysis of the program behaviour is futile.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • Thanks for answering, the code snippet is merely an example(maybe not a good one) to make you understand which part of the mechanism I don't understand. In a normal situation where I don't violate anything, how does the compiler know which of the functions inside the vtable to invoke? – katkato Jun 01 '21 at 13:34
  • 3
    @katkato That is a compiler specific detail, but often the vtable entries are in the order the virtual functions are first declared. In your code, `r->foo()` wants to call the first virtual function (because `foo` is first in `A`). With `r` pointing to a `B`, it looks this up in B's vtable and finds `B::goo`, which, since it has the same signature, might just work in this instance. – 1201ProgramAlarm Jun 01 '21 at 13:51