First let’s see a program
/*
Abstract : A program to show polymophism.
Author : Lijie Wang
Environment : Visual C++.NET 2005
History : 2006-10-29
*/
#include<iostream>
using namespace std;
class B1
{
public:
virtual void vf1()
{
cout<<"B1::vf1"<<endl;
}
virtual void vf2()
{
cout<<"B1::vf2"<<endl;
}
virtual void vf3()
{
cout<<"B1::vf3"<<endl;
}
void B 1f (){}
};
class B2
{
public:
virtual void vf2()
{
cout<<"B2::vf2"<<endl;
}
virtual void vf3()
{
cout<<"B2::vf3"<<endl;
}
virtual void vf4()
{
cout<<"B2::vf4"<<endl;
}
void B 2f (){}
};
class D : public B1, public B2
{
public:
void vf1() //overide B1::vf1 and B2::vf1
{
cout<<"D::vf1"<<endl;
}
void vf3()
{
cout<<"D::vf3"<<endl;
}
virtual void vf5() //additional virtual function
{
cout<<"D::vf5"<<endl;
}
void Df() //non virtual function
{
cout<<"D::Df"<<endl;
}
void Set_A(int n)
{
a = n;
cout<<"D::A="<<a<<endl;
}
private:
int a;
};
void main()
{
D d;
B1 b1;
B2 b2;
B1 *pb1 = &d; //pb1 point to the B1-sub-object of D
B2 *pb2 = &d; //pb1 point to the B2-sub-object of D
d.vf3();
d.vf5();
b1.vf1();
b2.vf3();
pb1->vf1(); //danymic binding
pb1->vf2(); //danymic binding
pb1->vf3();
dynamic_cast<D*>(pb1)->Set_A(1);
pb2->vf2();
pb2->vf3();
pb2->vf4();
dynamic_cast<D*>(pb1)->vf5();
}
The class diagram of this program is shown in Fig 1.
Fig 1. Class Diagram
The structure of d object can be found from the Watch Window while debugging, just like Fig 2:
Fig 2. Structure of d Object
We can find from the structure of the d object that it includes a B1 sub-object and a B2 sub-object with a __vfptr in each one. From the vftables we can see that D::vf1 has overridden the B1::vf3 and B2::vf3, just according to our expectation. But we can not find the new virtual function D::vf 5 in the vftables. There are only two __vfptrs in the d object and each belongs to a sub-object. But we know, to support polymorphism the D::vf5 should be in the __vfptr of d object. The only way to satisfy the need is to insert D::vf5 into one of the two __vfptrs of the sub-objects, but we find none.
Let’s see the address of d.vf5 from the Watch Window:
Fig 3. Address of d.vf5
This indicates that the address of d.vf5 is 0x00411208.
Then we can find the address of the vftable of B1 sub-object or say d.B1.__vfptr from the Watch Window:
Fig 4. Address of d.B1.__vfptr
So the address of the B1.__vfptr of d is 0x00417744, then we turn to the Memory Window to check what is stored in the vftable of B1 sub-object:
Fig 5. Memory content from of vftable of B1 sub object of d
From the picture above we can see that the four successive words stored in the vftable of B1 sub-object are: 0x004111e5, 0x00411091, 0x004111bd, 0x00411208. 0x00411208 is just the address of d.vf5, this indicates that d.vf5 is actually inserted in the end of the first sub-object vftable of d, here is vftable of B1 sub-object. But we can not find it from Fig 2. So I think this is a bug of the VC++.NET IDE (I use VC++.NET 2005).
Then the we modify the program by replacing each vf* function by f*. Then the structure of the d object in the Watch Window is shown in the Fig 6.
Fig 6. Structure of d Object After Modification