Reputation: 780
This is similar to many previous questions, but it asks something which I was not able to find answer.
#include <iostream>
using namespace std;
class Base1 {
public:
int b1_data;
virtual void b1_fn() {cout << "I am b1\n";}
};
class Base2 {
public:
int b2_data;
virtual void b2_fn() {cout << "I am b2\n";}
};
class Derived : public Base1, public Base2 {
public:
int d_data;
void b1_fn() {cout << "I am b1 of d\n";}
void b2_fn() {cout << "I am b2 of d\n";}
};
int main() {
Derived *d = new Derived();
Base1 *b1 = d;
/*My observation mentioned below is implementation dependant, for learning,
I assume, there is vtable for each class containing virtual function and in
case of multiple inheritance, there are multiple vtables based on number of
base classes(hence that many vptr in derived object mem layout)*/
b1->b1_fn(); // invokes b1_fn of Derived because d points to
// start of d's memory layout and hence finds vtpr to
// Derived vtable for Base1(this is understood)
Base2 *b2 = d;
b2->b2_fn(); // invokes b2_fn of Derived but how? I know that it "somehow"
// gets the offset added to d to point to corresponding Base2
// type layout(which has vptr pointing to Derived vtable for
// Base2) present in d's memory layout.
return 0;
}
Specifically, how does b2 point to vptr for vtable of Derived for Base2 to get to b2_fn()? I've tried seeing memlayout dump from gcc but couldn't figure out much.
Upvotes: 4
Views: 821
Reputation: 170
#include <iostream>
using namespace std;
class Base1 {
public:
virtual void b1_fn() {cout << "I am b1\n";}
};
class Base2 {
public:
virtual void b2_fn() {cout << "I am b2\n";}
};
class Derived : public Base1, public Base2 {
public:
void b1_fn() {cout << "I am b1 of d\n";}
void b2_fn() {cout << "I am b2 of d\n";}
};
int main() {
Base1 b1;
Base2 b2;
Derived d;
cout<<"size of base1 object"<<sizeof(b1)<<endl;
cout<<"size of base2 object"<<sizeof(b2)<<endl;
cout<<"size of derived object"<<sizeof(d)<<endl;
return 0;
}
Output of above program is: 8 8 16
So you can see that derived object has inherited two vptr from base1 and base2
both vptr in derived class points to vtable of derived class. since you have overriden the base methods in derived class so vptr will point to the derived class methods.
In case you had not overriden methods in derived class than vtable would be having address of base class methods and vptr would be pointing to those
Thats why b2->b2_fn(); calls derived method
Upvotes: 0
Reputation: 73446
The compiler in case of multiple inheritance, construct his vtables so that every subobject has an appropriate vtable. Of course this is implementation dependent (as the vtables themselves), but it would be organized like that:
Base1
object has a vptr pointing to a vtable containing a unique pointer to Base1::b1_fn
Base2
object has a vptr pointing to a vtable containing a unique pointer to Base2::b2_fn
Derived
object has a vptr pointing to a vtable that starts with the vtable layout corresponding to Base1
, but extendeds it with the missing elements of Base2
's vtable. With "Layout" I mean that the pointer for b1_fn()
is at the same offset, but it might point to an overriding function. So, here, the table would contain Derived::b1_fn
followed by Derived::b2_fn
. This combined layout ensures that the Base1
subobject in Derived
can share the vtable with its child. Derived
object is composed of 2 subobjects: so the Base1
subobject will be followed by a the Base2
subobject, which will have its own vtable, using the layout required for Base2
, but again with Base2::b2_fn
instead of the original one. When casting the Derived
pointer to a Base2
pointer, the compiler will make it point to the Base2
subobject with its vtable, by applying a fixed offset determined at compile time.
By the way if you would do a downcasting, the compiler would similarly use the fixed offset in the other direction, to find the start of the Derived
. This is all pretty simple until you use virtual bases, in which the technique of the fixed offset can no longer work. Virtual base pointers must then be used as explained in this other SO answer
This Dr.Dobb's article is the best explanation for all these layouts with some good pictures.
Upvotes: 4