I am studying VTable of c++ in university. And maybe I missed something, but I saw following example:
class Parent{
public:
int x;
Parent(int _x) : x(_x){}
virtual void print() const{
cout << x << '\n';
}
};
class Derived : public Parent{
public:
int y;
Derived(int _x, int _y) : Parent(_x), y(_y){}
void print() const override{
cout << x << ' ' << y << '\n';
}
};
int main()
{
Derived *a = new Derived(1, 2);
Parent *b = a;
b->print();
}
Here I learned that b's VTable pointer changes to a's VTable pointer, resulting printing "1 2", which seems reasonable. But I still can't understand why it won't work if I do the same thing with nonpointer:
class Parent{
public:
int x;
Parent(int _x) : x(_x){}
virtual void print() const{
cout << x << '\n';
}
};
class Derived : public Parent{
public:
int y;
Derived(int _x, int _y) : Parent(_x), y(_y){}
void print() const override{
cout << x << ' ' << y << '\n';
}
};
int main()
{
Derived a(1, 2);
Parent b = a;
b.print();
}
Why it prints "1", what I actually want to know is what will happen with VTable pointer. Does VTable pointer of b do not change to a's VTable pointer? Why?
Thank you.
I wonder why nobody dares to write an answer :-)
I summarize it a bit:
The short answer is "Nothing ever happens to vtable pointers".
Every class with virtual methods or derived from a class with virtual methods has its own vtable and every instance of that class gets a pointer to the vtable of its own class.
This vtable pointer inside of the instance will never change.
The key element is that the vtables of Parent
and Derived
look the same (they point to a print
implementation) but a caller does not need to know where the pointer goes to.
In the first example you only have one instance but two typed pointers to the same instance.
The typed pointers don't know the exact type of the target instance, but they know where to find the pointer to the correct print
method is inside of the instance vtable.
In the second example you create two instances, it copy-constructs b
from the content of a
(only the Parent
fields).
As the constructed b
is a Parent
, the instance also gets its own vtable which matches to a Parent
.
It might be more clear if you print
from both variables and let the print
methods output the address of the this
pointer
#include <iostream>
using namespace std;
class Parent{
public:
int x;
Parent(int _x) : x(_x){}
virtual void print() const{
cout << x << " at " << this << '\n';
}
};
class Derived : public Parent{
public:
int y;
Derived(int _x, int _y) : Parent(_x), y(_y){}
void print() const override{
cout << x << ' ' << y << " at " << this << '\n';
}
};
int main()
{
Derived *pa = new Derived(1, 2);
Parent *pb = pa;
Derived a(3, 4);
Parent b = a;
pa->print();
pb->print();
a.print();
b.print();
}
Example output:
1 2 at 0xd062b0
1 2 at 0xd062b0
3 4 at 0x7ffd557ea6a0
3 at 0x7ffd557ea690