C++ 的多态,使用动态绑定的技术,技术的核心是虚函数表(简称虚表),每个包含了虚函数的类都包含一个虚表,虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象共用一个虚表。
每个包含虚函数的类,都有一个虚表指针,虚表指针在数据结构的第一个,对象在创建时就有了这个指针,并且指向了该类的虚表。
#include
using namespace std;
class TBase {void fun1(){};void fun2(){};void fun3(){};int x;
};
class VTBase {virtual void fun1(){};virtual void fun2(){};virtual void fun3(){};int x;
};
int main()
{TBase tbase;VTBase vtbase;std::cout << sizeof(tbase) << endl;std::cout << sizeof(vtbase) << endl;
}
我们知道函数是不会占用类的空间的,所以第一个Tbase类大小始终为4。含有虚函数的类,无论有多少个虚函数,都会增加一个虚表指针的大小(x64:8,x86:4)。
另外可以通过offsetof来看偏移,以x64编译器为例子:
cout << offsetof(VTBase, x) << endl;//输出值为8
x偏移为8,是我们结构体中可见的首个变量,所以前8个字节为虚函数表指针的大小,也是该类的第一个成员变量。

这里还是以X64编译器举例
typedef long long Vtpr_type;typedef void (*VFunc)(void); //定义一个函数指针类型变量类型 VFunc
VTBase* v_base = new VTBase();//将对象的首地址输出Vtpr_type* vptr = nullptr;//vptr是虚函数类的第一个成员变量,也是虚函数表的指针memcpy(&vptr, v_base, sizeof(Vtpr_type));
VFunc funa = (VFunc)(*vptr);VFunc funb = (VFunc)(*(vptr + 1));VFunc func = (VFunc)(*(vptr + 2));funa();//VTBase::a()funb();//VTBase::b()func();//VTBase::c()
完整代码
#include
using namespace std;
namespace {class VTBase {public:virtual void a() { cout << "VTBase::a()" << endl; }virtual void b() { cout << "VTBase::b()" << endl; }virtual void c() { cout << "VTBase::c()" << endl; }int x, y;};typedef long long Vtpr_type;typedef void (*VFunc)(void); //定义一个函数指针类型变量类型 VFunc} // namespaceint main()
{//offsetofVTBase vtbase;std::cout << sizeof(vtbase) << endl;cout << offsetof(VTBase, x) << endl;cout << offsetof(VTBase, y) << endl;/*x偏移为8,y偏移为12所以前8个字节为虚函数表的大小,也是该类的第一个成员变量*/VTBase* v_base = new VTBase();//将对象的首地址输出Vtpr_type* vptr = nullptr;//vptr是虚函数类的第一个成员变量,也是虚函数表的指针memcpy(&vptr, v_base, sizeof(Vtpr_type));//虚函数的地址存储在了虚函数表中,虚函数表地址内的值,为虚函数的指针VFunc funa = (VFunc)(*vptr);VFunc funb = (VFunc)(*(vptr + 1));VFunc func = (VFunc)(*(vptr + 2));funa();funb();func();delete v_base;v_base = nullptr;return 0;
}
void TestVtprValue() {VTBase* v_base1 = new VTBase();VTBase* v_base2 = new VTBase();Vtpr_type* vptr1 = nullptr;Vtpr_type* vptr2 = nullptr;memcpy(&vptr1, v_base1, sizeof(Vtpr_type));memcpy(&vptr2, v_base2, sizeof(Vtpr_type));cout << vptr1 << endl;cout << vptr2 << endl;}

我们先定义一个派生类VTBaseExt,并且重写其中某一个虚函数
class VTBaseExt : public VTBase {public:virtual void b() { cout << "VTBaseExt::b()" << endl; }
};
void TestVtbExt()
{VTBase* v_base = new VTBase();Vtpr_type* vptr_base = nullptr;memcpy(&vptr_base, v_base, sizeof(Vtpr_type));cout << vptr_base << endl;VTBaseExt* v_base_ext = new VTBaseExt();Vtpr_type* vptr_ext = nullptr;memcpy(&vptr_ext, v_base_ext, sizeof(Vtpr_type));cout << vptr_ext << endl;VFunc funa = (VFunc)(*(vptr_ext));VFunc funb = (VFunc)(*(vptr_ext + 1));VFunc func = (VFunc)(*(vptr_ext + 2));funa();//VTBase::a()funb();//VTBaseExt::b()func();//VTBase::c()
}
此时执行,我们发现VTBase、VTBaseExt虚表大小均为4,但虚表数组中储存的第二个值发生了变化

从上图可知是同一个

获取虚函数表数组存储的函数地址
void TestVtbExt()
{VTBase* v_base = new VTBase();Vtpr_type* vptr_base = nullptr;memcpy(&vptr_base, v_base, sizeof(Vtpr_type));cout << vptr_base << endl;VTBaseExt* v_base_ext = new VTBaseExt();Vtpr_type* vptr_ext = nullptr;memcpy(&vptr_ext, v_base_ext, sizeof(Vtpr_type));cout << vptr_ext << endl;//获取虚函数表数组地址for (int i = 0; i < 3; i++) {cout << *(vptr_base + i) << " " << *(vptr_ext + i) << endl;}
}
从图中可以看出来,只有被重写过的第二组是不一样的,其余的函数地址都没有变化

调用代码释义:
void TestVtbPointer() {//基类指针指向自己VTBase* v_base = new VTBase();Vtpr_type* vptr_base = nullptr;memcpy(&vptr_base, v_base, sizeof(Vtpr_type));cout << vptr_base << endl;//派生类指针指向派生类VTBaseExt* v_base_ext = new VTBaseExt();Vtpr_type* vptr_ext = nullptr;memcpy(&vptr_ext, v_base_ext, sizeof(Vtpr_type));cout << vptr_ext << endl;//基类指针指向派生类VTBase* v_base_to_ext = new VTBaseExt();Vtpr_type* vptr_to_ext = nullptr;memcpy(&vptr_to_ext, v_base_to_ext, sizeof(Vtpr_type));cout << vptr_to_ext << endl;
}
从结果输出来看,最终的基类指针指向派生类对象,虚函数表的指针还是指向了派生类的虚函数表

动态绑定的最终实现就是查虚函数表,每个对象的类对应了自己的虚函数表,虚函数表存储了与之对应的虚函数地址。
class VTBase {
public:virtual void func_1() { cout << __FUNCTION__ << endl; }virtual void func_2() { cout << __FUNCTION__ << endl; }virtual void func_3() { cout << __FUNCTION__ << endl; }int x;
};
class VTBaseExt : public VTBase {
public:virtual void func_2() { cout << "VTBaseExt:: " << __FUNCTION__ << endl; }
};
class VTBaseFinal : public VTBaseExt {
public:virtual void func_4() { cout << "VTBaseFinal:: " << __FUNCTION__ << endl; }
};
