You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

虚函数表(vtable)如何处理多继承?基于单继承内存布局的问询

多继承场景下的虚函数表处理

好问题!多继承搭配虚函数的情况确实比单继承要绕一些,咱们结合你给出的例子展开说清楚:

假设我们的代码是这样的:

class Genius { 
    int IQ; 
public: 
    virtual void brag(); 
};
class CoolDude { 
    int charm; 
public: 
    virtual void showOff(); 
};
class Me : public Genius, public CoolDude { 
    int age; 
public:
    // 重写两个基类的虚函数
    void brag() override;
    void showOff() override;
};

内存布局变化

和单继承只带一个虚表指针(vptr)不同,每个拥有虚函数的基类,都会在子类实例中对应一个独立的虚表指针。所以Me实例的内存布局大概是这样的:

  • 第一个位置:对应Genius的虚表指针(vptr1),指向Genius相关的虚函数表
  • 紧接着:Genius的成员变量int IQ
  • 然后:对应CoolDude的虚表指针(vptr2),指向CoolDude相关的虚函数表
  • 紧接着:CoolDude的成员变量int charm
  • 最后:Me自己的成员变量int age

虚函数调用的逻辑

编译器会帮我们处理好不同基类虚函数的调用细节:

  • 当通过Me*Genius*指针调用brag()时,直接用第一个虚表指针(vptr1)去对应的虚表里找函数入口,此时this指针指向实例的起始位置,不需要调整。
  • 当通过CoolDude*指针调用showOff()时,编译器会自动调整this指针——把它偏移到Me实例中CoolDude部分的起始位置(也就是vptr2的位置),再用这个vptr2去对应的虚表里找showOff()的入口。

如果子类重写了某个基类的虚函数,对应的虚表条目会被替换成子类重写后的函数地址。比如Me::brag()会覆盖vptr1指向的虚表里brag()的条目,Me::showOff()会覆盖vptr2指向的虚表里showOff()的条目。

额外补充:菱形继承的特殊情况

如果遇到菱形继承(比如GeniusCoolDude都继承自同一个带虚函数的基类Person),此时需要用到虚继承来避免基类成员的重复存储,这时除了虚表指针,还会引入虚基表(vbtable)来记录虚基类的位置偏移,不过这是更复杂的衍生场景啦。

内容的提问来源于stack exchange,提问作者user9478968

火山引擎 最新活动