上文在C++对象模型中,提到了vptl和vptr。写这篇文章即是在代码验证这二者。Talk is cheap。
平台:Win7 x64,编译:dev Cpp。
非继承下的vptr和vptl
#include <iostream>
using namespace std;
class Base
{
public:
Base(int i) : baseInt(i) { }
~Base() { cout << "Base::~Base()" << endl; }
virtual void Base_virtual_func1() {
cout << "Base::Base_virtual_func1" << endl;
}
virtual void Base_virtual_func2() {
cout << "Base::Base_virtual_func2" << endl;
}
private:
int baseInt;
};
typedef void (*Fun)();
void test1()
{
Base base(1000);
cout << "对象起始地址:" << &base << endl;
cout << "虚函数表的首地址: " << (int*)*(int*)(&base) << endl;
cout << "虚函数1的地址: " << (int*)*(int*)(&base) << " ";
Fun ptr1 = (Fun)*(int*)*(int*)(&base);
ptr1();
cout << "虚函数2的地址: " << (int*)*(int*)(&base) + 2<< " ";
Fun ptr2 = (Fun)*((int*)*(int*)(&base) + 2);
ptr2();
cout << "---------------" << endl;
}
int main()
{
test1();
}
输出结果:
从结果可以看出:Vptr 是放在对象起始地址。关系如图:
单继承下的vptr和vptl
#include <iostream>
using namespace std;
typedef void (*Fun)();
class Base
{
public:
Base(int i) : baseInt(i) { }
~Base() { cout << "Base::~Base()" << endl; }
virtual void virtual_func() {
cout << "Base::virtual_func" << endl;
}
virtual void Base_virtual_func() {
cout << "Base::Base_virtual_func" << endl;
}
private:
int baseInt;
};
class Derive : public Base
{
public:
Derive(int d): Base(1000), DeriveInt(d) { }
~Derive() { cout << "Derive::~Derive()" << endl; }
void virtual_func() override {
cout << "Drive::virtual_func()" << endl;
}
virtual void Drive_virtual_func() {
cout << "Drive::Drive_virtual_func" << endl;
}
private:
int DeriveInt;
};
void test2()
{
Derive derive(2000);
cout << "对象起始地址:" << &derive << endl;
cout << "虚函数表首地址:" << (int*)*(int*)(&derive) << endl;
cout << "虚函数1的地址:" << (int*)*(int*)(&derive) << " ";
Fun ptr1 = (Fun)*(int*)*(int*)(&derive);
ptr1();
cout << "虚函数2的地址:" << (int*)*(int*)(&derive) + 2 << " ";
Fun ptr2 = (Fun)*((int*)*(int*)(&derive) + 2);
ptr2();
cout << "虚函数3的地址:" << (int*)*(int*)(&derive) + 4 << " ";
Fun ptr3 = (Fun)*((int*)*(int*)(&derive) + 4);
ptr3();
cout << endl << "----------------------" << endl;
}
int main()
{
test2();
}
输出结果:
从结果来看,virtual function一共有3种可能:
- 继承base class的virtual function函数实例。
- 可以使用覆盖掉base class的virtual function。
- 它可以加一个新的virtual fucntion。这时候virtual table的尺寸会增加一个新的slot,新的函数实例地址会放在其中。
如图所示:
多继承下的vptr和vptl
子类不覆盖父类的虚函数
#include <iostream>
using namespace std;
typedef void(*Fun)();
class Base1{
public:
virtual void f() { cout << "Base1::f" << endl; }
virtual void g() { cout << "Base1::g" << endl; }
};
class Base2{
public:
virtual void f() { cout << "Base2::f" << endl; }
virtual void g() { cout << "Base2::g" << endl; }
};
class Base3{
public:
virtual void f() { cout << "Base3::f" << endl; }
virtual void g() { cout << "Base3::g" << endl; }
};
class Derive : public Base1, public Base2, Base3{
public:
virtual void a() { cout << "Derive::a" << endl; }
virtual void b() { cout << "Derive::b" << endl; }
};
void v_table_test4()
{
Derive d;
Fun func_ptr1 = nullptr;
Fun func_ptr2 = nullptr;
Fun func_ptr4 = nullptr;
Fun func_ptr5 = nullptr;
Fun func_ptr7 = nullptr;
Fun func_ptr8 = nullptr;
Fun func_ptr10 = nullptr;
Fun func_ptr11 = nullptr;
cout << "对象起始地址: " << (int*)(&d) << endl;
cout << "第1个vptr地址:" << (int*)(&d) << endl;
cout << "第1个虚函数表首地址: " << (int*)*(int*)(&d) << endl;
func_ptr1 = (Fun)*( (int*)*(int*)(&d));
func_ptr2 = (Fun)*( (int*)*(int*)(&d) + 2);
func_ptr4 = (Fun)*( (int*)*(int*)(&d) + 4);
func_ptr5 = (Fun)*( (int*)*(int*)(&d) + 6);
func_ptr1();
func_ptr2();
func_ptr4();
func_ptr5();
cout << "----------------" << endl;
cout << "第2个vptr地址:" << (int*)(&d) + 2 << endl;
cout << "第2个(vptr->vptl)虚函数表首地址: " << (int*)*( (int*)(&d) + 2) << endl;
func_ptr7 = (Fun)*( (int*)*( (int*)(&d) + 2));
func_ptr8 = (Fun)*( ((int*)*( (int*)(&d) + 2)) + 2);
func_ptr7();
func_ptr8();
cout << "----------------" << endl;
cout << "第3个vptr地址:" << (int*)(&d) + 4 << endl;
cout << "第3个(vptr->vptl)虚函数表首地址: " << (int*)*( (int*)(&d) + 4) << endl;
func_ptr10 = (Fun)*( (int*)*( (int*)(&d) + 4));;
func_ptr11 = (Fun)*( ((int*)*( (int*)(&d) + 4)) + 2);
func_ptr10();
func_ptr11();
}
int main()
{
v_table_test4();
}
输出结果:
从结果看来,对于子类不覆盖父类的虚函数的多继承,子类有多个vptr和多个vptl,每一个vptr指向一个vptl,子类自己的虚函数在第一个vptl里面,
如图所示:
子类覆盖自己的虚函数
这个类似单继承下的vptr和vptl的,只是替换相应的函数地址。
多重继承和虚继承
比较复杂,暂时还不会。