文章目录
- 1.一道面试题 -- 引入虚函数表
- 2.多态是怎么实现的呢?
- 2.1满足多态的时候🐧
- 2.2不满足多态的时候🐧
- 3.打印虚函数表
1.一道面试题 – 引入虚函数表
①🍎 _vfptr 虚表指针,虚表指针是用来实现多态的 (多态的原理
)
对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有
虚函数的类中都至少都有一个虚函数表指针
,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表;
②🍎 这里常考一道笔试题:sizeof(Base)是多少? (8个字节)
2.多态是怎么实现的呢?
2.1满足多态的时候🐧
①🐧满足多态的时候,程序运行时才会去虚函数表中找对应的虚函数调用,指向父类调父类,指向子类调子类;
运行时
Person
和Student
不同类型都有不同的虚表,同类型的虚表相同;
②🐧对重写
这个概念进一步了解:
基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖
,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
③🐧派生类的虚表生辰过程:
🍎a.先将基类中的虚表内容拷贝一份
到派生类虚表中;
🍎b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖
虚表中基类的虚函数 ;
🍎c.派生类自己新增加的虚函数按其在派生类中的声明次序
增加到派生类虚表的最后;
2.2不满足多态的时候🐧
①🐧不满足多态的时候,就不能达到指向谁调用谁,在编译的时候,就根据对象的类型进行调用了;
3.打印虚函数表
易错点❗❗❗:
①🐧只有相近的数据类型才能转换 (不可以从自定义类型强制转换成为整形);
#include <iostream>
using namespace std;
class Base {
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
private:
int a = 1;
};
class Derive :public Base {
public:
virtual void func1() { cout << "Derive::func1" << endl; }
virtual void func3() { cout << "Derive::func3" << endl; }
virtual void func4() { cout << "Derive::func4" << endl; }
private:
int b = 2;
};
// 打印对象虚表
typedef void(*VFPTR)();
// 打印函数指针数组
// virtual function table
//void PrintVFT(VFPTR vft[])
void PrintVFT(VFPTR* vft)
{
for (size_t i = 0; i < 4; i++)
{
printf("%p->", vft[i]);
VFPTR pf = vft[i];
(*pf)();
//pf();
}
}
int main()
{
Base b;
Derive d;
//int ptr = (int)d; // 不支持转换,只有有关联的类型才能互相转
//注意:地址也可以理解成为数据
VFPTR* ptr = (VFPTR*)(*((int*)&d));
PrintVFT(ptr);
// 函数指针
void (*p1)();
VFPTR p2;
// 函数指针数组
void (*pa1[10])();
VFPTR pa2[10];
return 0;
}