1.虚函数怎么实现多态的
参考 https://zhuanlan.zhihu.com/p/47057750
- 图中vptr是虚表指针,所指向的虚函数表在右边,内部包含两个虚函数
- 当子类覆写父类虚函数时,替换虚函数指针为覆写的函数对应的指针,最后不同子类的虚表中虚函数指向不同的函数
多态的实现:基类指针或引用指向子类时,通过虚表查找对应的虚函数进行调用,如果所有子类虚函数是覆写的,就可以实现不同子类调用不同函数的效果
2.只有虚类才有虚表(包含虚函数的类才会有虚表)
3.获取虚函数指针,进而调用虚函数
#include <iostream>
using namespace std;
#pragma pack(1) // 类成员字节对齐单位1字节, 即不进行字节对齐
class Base
{
public:
virtual void Hello() = 0;
virtual int Plus(int a, int b) = 0;
};
class A :public Base
{
public:
virtual void Hello() override {
cout<<"A: "<<"hello."<<endl;
}
virtual int Plus(int a, int b) override {
cout<<"A: "<<a+b<<endl;
return a+b;
}
public:
int a {0};
};
class B :public Base
{
public:
void Hello() override{
cout<<"B: "<<"hello."<<endl;
}
int Plus(int a, int b) override {
cout<<"B: "<<a*b<<endl;
return a*b;
}
public:
int b{0};
};
class C{
int a { 0 };
};
class D{
int a { 0 };
virtual void aa(){}
};
int main() {
// 只有虚类才有虚表指针
C c;
D d;
cout<<"sizeof c: "<<sizeof(c)<<endl;
cout<<"sizeof d: "<<sizeof(d)<<endl; // 虚类多一个虚表指针,该指针大小和机器位数有关
cout<<"sizeof(intptr_t*):"<<sizeof(intptr_t*)<<endl; // 64位机器对应虚表指针8字节
cout<<"================"<<endl;
A aa;
B bb;
Base* base = &aa;
base->Hello();
base = &bb;
base->Hello();
// 通过虚表指针调用虚函数
typedef void(* _hello)(void);
typedef int(* _plus)(int, int);
// 虚表地址 *(intptr_t*)(&aa) 做强转获取虚表指针 (intptr_t*), 最后解除引用得到虚函数指针,指向函数 Hello()
int v_addr = *(intptr_t *)(&aa); // 虚表地址
intptr_t* v_form = (intptr_t*)v_addr; // 虚表指针, 虚表指针和虚函数指针的关系是 虚函数指针 = *(虚表地址+对应的位置偏移)
_hello v_hello = reinterpret_cast<_hello> (*v_form); // 虚函数指针刚好指向A::Hello()
v_hello(); // 通过虚函数指针调用虚函数 A::Hello()
// (虚表内虚函数指针都是(intptr_t*) 大小), 偏移虚函数指针指向虚表内下一虚函数
_plus v_plus = reinterpret_cast<_plus>(*(v_form+1));
int val = v_plus(2,3);
cout<<"val:"<<val<<endl;
return 0;
}
- 如果类A定义了虚函数,那么对应的对象a的内存第一个成员就是虚表指针,对类对象的指针作强转,然后解引用就可以获得虚表地址
-
int v_addr = *(intptr_t *)(&aa); // 虚表地址 intptr_t* v_form = (intptr_t*)v_addr; // 虚表指针, 虚表指针和虚函数指针的关系是 虚函数指针 = *(虚表地址+对应的位置偏移)
而虚表指针和虚函数地址是 指针 和 对该指针解引用 的关系;因此可以获得虚函数指针,直接调用对应类的虚函数。