C++ 函数语义学——虚函数地址问题的 vcall 引入

vcall 是什么?

vcall 是编译器生成的内容,完整的名字应该叫 vcall thunk。当通过基类指针或引用调用虚函数时,编译器会通过虚函数表找到实际要调用的函数地址。这种机制被称为虚函数调用(vcall)。

示例代码

以下是一个示例,展示了虚函数调用的工作原理:

#include <iostream>

class Base {
public:
    virtual void myFunction() {
        std::cout << "Base::myFunction" << std::endl;
    }
};

class Derived : public Base {
public:
    void myFunction() override {
        std::cout << "Derived::myFunction" << std::endl;
    }
};

int main() {
    Derived myacls;
    myacls.myFunction(); // 直接调用,不通过虚函数表

    Base* pmyacls = &myacls;
    pmyacls->myFunction(); // 通过虚函数表查找并调用

    Base& ref = myacls;
    ref.myFunction(); // 通过虚函数表查找并调用

    myacls.Base::myFunction(); // 明确调用基类的虚函数,等价于调用普通成员函数

    return 0;
}

解释

  • 直接调用myacls.myFunction() 直接调用派生类的函数,不通过虚函数表。
  • 通过指针调用pmyacls->myFunction() 通过基类指针调用虚函数,编译器会通过虚函数表指针找到虚函数表,然后通过虚函数表找到实际要调用的函数地址。
  • 通过引用调用ref.myFunction() 通过基类引用调用虚函数,过程与通过指针调用类似。
  • 明确调用基类函数myacls.Base::myFunction() 明确调用基类的虚函数,等价于调用普通成员函数,不通过虚函数表。

虚函数调用的本质

  • 虚函数表指针(vptr):每个对象实例都有一个虚函数表指针,指向其类的虚函数表。
  • 虚函数表(vtable):虚函数表中存储了该类的虚函数地址。
  • 虚函数调用(vcall):通过基类指针或引用调用虚函数时,编译器会通过对象的虚函数表指针找到虚函数表,然后通过虚函数表找到实际要调用的函数地址。

vcall thunk 的作用

  1. 调整 this 指针:在多重继承的情况下,例如一个子类继承父类1和父类2,如果父类2的指针要调用子类的析构函数,那这个父类2的指针就需要利用 vcall thunk 机制调整 this 指针。
  2. 跳到真正的虚函数中去vcall thunk 负责跳转到实际的虚函数实现。

总结

虚函数调用通过虚函数表和虚函数表指针实现多态性。编译器在运行时通过虚函数表找到实际要调用的函数地址,从而实现动态绑定。这种机制使得派生类可以重写基类的虚函数,并通过基类指针或引用调用派生类的实现。vcall thunk 进一步增强了这种机制,通过调整 this 指针和跳转到实际的虚函数实现,确保了多重继承和复杂继承结构中的正确性。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值