31.C++多态4(静态多态,动态多态,虚函数表的存储位置)
⭐上篇文章:30.C++多态 3 (多态的原理,虚指针,虚函数表,抽象类)-CSDN博客
⭐本篇代码:c++学习/17.C++三大特性-多态 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)
⭐标⭐是比较重要的部分
目录
一. 静态多态与动态多态
二. 派生类的虚表的内存分布
2.1 单继承派生类虚表的分布
2.2 多继承下派生类虚表的分布
一. 静态多态与动态多态
静态多态是在编译的时候就确定并绑定好了的,比如函数重载,模板的使用。
动态多态是在运行期间再绑定的,当调用的时候才去确定函数的地址
关于模板的使用可以看我的这些文章:
17. C++模板(template)1(泛型编程,函数模板,类模板)_c++ 17 template-CSDN博客
24. C++模板 2 (非类型模板参数,模板的特化与模板的分离编译)-CSDN博客
二. 派生类的虚表的内存分布
2.1 单继承派生类虚表的分布
在上篇文章中,我们知道对于派生类来说。它会先将父类的虚表拷贝一份,如果自己有重写虚函数,就会将重写的虚函数覆盖到虚表中,如果有新的虚函数,就会添加到原本虚表的末尾。
我们通过代码来输出虚函数的地址来观察一下:
通过解引用虚函数指针来获取虚函数表中的内容
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//单继承
class A
{
public:
virtual void f1() { cout << "A::f1" << endl; }
virtual void f2() { cout << "A::f2" << endl; }
private:
int a = 1;
};
class B :public A
{
public:
virtual void f1() { cout << "B::f1" << endl; }
//f2没有重写被直接继承
virtual void f3() { cout << "B::f3" << endl; }
virtual void f4() { cout << "B::f4" << endl; }
private:
int b = 1;
};
//void(*p)();//函数指针
typedef void(*VF_PTR)(); //重定义函数指针的名称
void PrintFTable(VF_PTR* table,int size) //函数指针数组(存函数指针的数组)
{
for (int i = 0; i < size; ++i)
{
printf("vftable[%d]:%p\n", i, table[i]);
table[i]();
}
cout << endl;
}
//单继承虚函数表
void test1()
{
A a;
B b;
//取虚函数表的地址(前四个字节)
cout << "a的虚表:" << endl;
PrintFTable((VF_PTR*)(*(int*)&a), 2);
cout << "a的虚表:" << endl;
PrintFTable((VF_PTR*)(*(int*)&b), 4);
}
int main()
{
test1();
return 0;
}
输出结果如下:
可以看到,由于f2没有被重写,所以两个虚表中f2的地址是一样的!
2.2 多继承下派生类虚表的分布
如果一个派生类有两个父类,那么它会有两张虚表。如果派生类重写了父类的虚函数,同样会在相应的虚表中进行覆盖(比如 重写了A函数,且A函数是来自类Person中的,那么就会在Person这个虚表中进行覆盖),
如果是新增的虚函数,则只会写入到第一张表中。
如果某一个函数在多个父类中都存在,那么重写这个虚函数的话就会覆盖所有表中的这个虚函数
测试代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//请在32位下运行此程序!
//单继承
class A
{
public:
virtual void f1() { cout << "A::f1" << endl; }
virtual void f2() { cout << "A::f2" << endl; }
private:
int a = 1;
};
//多继承
class C
{
public:
virtual void f1() { cout << "C::f1" << endl; }
virtual void f2() { cout << "C::f2" << endl; }
private:
int c = 1;
};
class D :public A, public C
{
public:
virtual void f1() { cout << "D::f1" << endl; }
virtual void f3() { cout << "D::f3" << endl; }
private:
int d = 1;
};
typedef void(*VF_PTR)();
void PrintFTable(VF_PTR* table, int size)
{
for (int i = 0; i < size; i++)
{
printf("vftable[%d]:%p\n", i, table[i]);
table[i]();
}
cout << endl;
}
//多继承虚函数表
void test2()
{
A a;
C c;
D d;
cout << sizeof(d) << endl;//32位下为20,64位下位40
PrintFTable((VF_PTR*)(*(int*)&a), 2);
PrintFTable((VF_PTR*)(*(int*)&c), 2);
PrintFTable((VF_PTR*)(*(int*)&d), 3); //表1的位置
PrintFTable((VF_PTR*)(*(int*)((char*)&d + sizeof(A))), 2); //表2的位置
}
//此时子类有两个虚表,分别存在第一个基类,第二个父基类。
//且子类自己的虚函数位于第一个基类的虚表中
int main()
{
test2();
return 0;
}
运行结果如下:
可以看到,多继承的派生类有两张表,并且重写的虚函数f1会进行覆盖 ,而且新增的虚函数f3会添加到第一张表中。