关于虚函数表和虚函数指针的探索

1.虚函数怎么实现多态的

参考 https://zhuanlan.zhihu.com/p/47057750

          

  1. 图中vptr是虚表指针,所指向的虚函数表在右边,内部包含两个虚函数
  2. 当子类覆写父类虚函数时,替换虚函数指针为覆写的函数对应的指针,最后不同子类的虚表中虚函数指向不同的函数

        多态的实现:基类指针或引用指向子类时,通过虚表查找对应的虚函数进行调用,如果所有子类虚函数是覆写的,就可以实现不同子类调用不同函数的效果

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;
}
  1. 如果类A定义了虚函数,那么对应的对象a的内存第一个成员就是虚表指针,对类对象的指针作强转,然后解引用就可以获得虚表地址
  2. int v_addr = *(intptr_t *)(&aa);      // 虚表地址
        intptr_t* v_form = (intptr_t*)v_addr; // 虚表指针, 虚表指针和虚函数指针的关系是  虚函数指针 = *(虚表地址+对应的位置偏移)

    而虚表指针和虚函数地址是 指针 和 对该指针解引用 的关系;因此可以获得虚函数指针,直接调用对应类的虚函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值