总结放在前面:
继承体系中就是有几个父类就有几张虚表
单继承只有一张虚表,继承下来的父类的虚表被用做子类的虚表了(或者说 继承的父类和子类共用一块虚表空间,父类的虚函数写在前面,子类的在后面)
多继承是有多少个父类就有多少虚表,其中第一个父类的虚表被用做子类的虚表的(原理和上面单继承的一样)
其中,子类新增的虚函数放在第一张虚表后面(即子类的虚表后面)
分四种情况演示:↓
下面我将使用程序打印 子类中虚表的内容:包括虚表本身的地址 与 虚表中各个虚函数的地址
目的是为了观察 子类的虚表中,父类的虚表和子类的虚表相对存储位置
声明:
1、我下面程序实在 32位系统下,指针大小为 4 位,因此直接将对象地址强转成 int* ,使其获取 前 4 个字节的数据(即一个int大小),虚函数表地址一般放在一个对象存储空间的开头,这4个字节直接就获取到 虚表地址了
2、关于为什么要定义函数指针及其使用介绍:因为虚函数表,是一个 函数指针 数组,因此要定义函数指针来取 该数组的元素,又因为该数组名就是该数组的首地址,指针的指针就是 (FunPtr*)
1.单继承无虚函数覆盖:
// 父类
class Base
{
public:virtual void Func1(){cout << "Base::Func1" << endl;}virtual void Func2(){cout << "Base::Func2" << endl;}};// 子类
class Derive :public Base
{
public:virtual void Func3(){cout << "Derive::Func3" << endl;}virtual void Func4(){cout << "Derive::Func4" << endl;}
};typedef void(*FunPtr)(); // 定义函数 void Fuc() 的函数指针 :函数类型 void ()(....形参列表),函数指针位置在函数名的位置,void ( *p )(....形参列表)
//打印虚函数表
void PrintVF(FunPtr* VFaddress)
{cout << "虚表地址:" << VFaddress << endl;int i = 1;FunPtr vfunc = VFaddress[0];while (vfunc != nullptr){cout << "第 " << i << " 个虚函数地址:" << vfunc << endl;vfunc();cout << '\n';VFaddress++;vfunc = *VFaddress;i++;}cout << "\n\n";
}int main()
{Base b;Derive d;cout << "父类虚表:" << "\n\n";PrintVF((FunPtr*)*(int*)&b); // *(int*)&b :将对象b的头4个字节的数据取出,这就是对象 b 的虚表地址// 将虚表地址强转成函数指针类型 FunPtr*cout << "子类虚表:" << endl;PrintVF((FunPtr*)*(int*)&d);system("pause");return 0;
}
在对象的存储中,虚表指针地址在存储空间的开头
单继承时,子类中只有一个虚函数表,父类虚函数在前,子类虚函数在后,按照声明顺序存放。
子类和父类共用同一张虚表,父类指向这张虚表的开头,子类虚表指针也指向这张虚表开头
只是 父子类虚表指针可以访问的范围不同,父子类都被准许访问自己区域的数据
2.单继承有虚函数覆盖
// 父类
class Base
{
public:virtual void Func1(){cout << "Base::Func1" << endl;}virtual void Func2(){cout << "Base::Func2" << endl;}};// 子类
class Derive :public Base
{
public:virtual void Func1(){cout << "Derive::Func1" << endl;}virtual void Func2(){cout << "Derive::Func2" << endl;}
};typedef void(*FunPtr)(); // 定义函数 void Fuc() 的函数指针 :函数类型 void ()(....形参列表),函数指针位置在函数名的位置,void ( *p )(....形参列表)
//打印虚函数表
void PrintVF(FunPtr* VFaddress)
{cout << "虚表地址:" << VFaddress << endl;int i = 1;FunPtr vfunc = VFaddress[0];while (vfunc != nullptr){cout << "第 " << i << " 个虚函数地址:" << vfunc << endl;vfunc();cout << '\n';VFaddress++;vfunc = *VFaddress;i++;}cout << "\n\n";
}int main()
{Base b;Derive d;cout << "父类虚表:" << "\n\n";PrintVF((FunPtr*)*(int*)&b); // *(int*)&b :将对象b的头4个字节的数据取出,这就是对象 b 的虚表地址// 将虚表地址强转成函数指针类型 FunPtr*cout << "子类虚表:" << endl;PrintVF((FunPtr*)*(int*)&d);system("pause");return 0;
}
子类中有一个虚函数表,覆盖的虚函数放在虚函数表中继承于父类的位置,新增的虚函数放在继承的父类虚表的后面。(即 重写的虚函数 直接放到继承的父类虚表 后面,共用同块空间)
3.多继承没有虚函数重写:
多继承时,子类中虚表个数等于直接父类的个数,子类中新增的虚函数放在第一个父类虚表末尾,按照声明顺序存放。
class Base1
{
public:virtual void Func1(){cout << "Base1::Func1" << endl;}virtual void Func2(){cout << "Base1::Func2" << endl;}};class Base2
{
public:virtual void Func1(){cout << "Base2::Func1" << endl;}virtual void Func2(){cout << "Base2::Func2" << endl;}};class Derive :public Base1, Base2
{
public:virtual void Func3(){cout << "Derive::Func3" << endl;}virtual void Func4(){cout << "Derive::Func4" << endl;}};typedef void(*FucPtr)();
void PrintVF(FucPtr* VFaddress)
{cout << "虚表地址:" << VFaddress << endl;int i = 1;FucPtr vfunc = VFaddress[0];while (vfunc != nullptr){cout << "第" << i << "个虚函数地址:" << vfunc << endl;vfunc();VFaddress++;vfunc = *VFaddress;i++;}cout << '\n';
}int main()
{Base1 b1;Base2 b2;Derive d;cout << "父类虚表(Base1):" << endl;PrintVF((FucPtr*)*(int*)&b1);cout << "父类虚表(Base2):" << endl;PrintVF((FucPtr*)*(int*)&b2);cout << "Derive ::Base1:" << endl;PrintVF((FucPtr*)*(int*)&d);cout << "Derive ::Base2:" << endl;PrintVF((FucPtr*)(*(int*)((char*)&d + sizeof(Base1))));system("pause");return 0;
}
4.多继承有虚函数重写时
多继承有虚函数重写时,子类中虚表个数等于直接父类的个数,子类中重写的虚函数覆盖继承的父类的虚函数,子类中新增的虚函数放在第一个直接父类的虚表末尾,按照声明顺序存放
参考文章:
c++中的虚函数及虚函数表