You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++虚函数绑定与虚表机制解析:为何输出Hello Derived?

解析:为什么这段C++代码输出Hello Derived?

先把可编译的代码贴出来方便参考(原代码漏了必要的头文件,我补上了):

#include <iostream>
using namespace std;

class Base { 
public: 
    int a; 
    virtual void fun1() { cout<<"Hello Base"<<endl; } 
    void fun2() { fun1(); } 
};
class Derived: public Base { 
    int a; 
    void fun1() { cout<<"Hello Derived"<<endl; } 
};
int main() { 
    Base * B = new Derived; 
    B->fun2(); 
    return 1; 
}

一、核心原因:虚函数的动态绑定

虽然fun2是Base类的非虚成员函数,编译器会对它做静态绑定,但fun2内部调用的fun1是虚函数——这才是关键!当B指向一个Derived对象时,运行时会根据对象的实际类型,调用Derived重写后的fun1,而不是Base的版本。

二、函数绑定的完整过程

咱们一步步捋运行时的逻辑:

  1. 编译阶段:B->fun2()的静态绑定
    因为fun2不是虚函数,编译器在编译时就能确定:不管B指向什么对象,B->fun2()调用的一定是Base::fun2。这就是静态绑定(编译时绑定)。

  2. 运行阶段:fun1()的动态绑定
    当Base::fun2执行到fun1();时,这里的fun1等价于this->fun1()。而this指针的实际指向是Derived对象(因为B指向的是new Derived)。
    由于fun1是虚函数,编译器不会直接调用Base::fun1,而是通过this指向的对象里的虚表指针(vptr),去查找对应的**虚函数表(vtable)**中的fun1条目。此时this指向的Derived对象的vptr指向Derived的vtable,里面的fun1条目是Derived::fun1的地址,所以最终调用的是Derived的版本。

三、Base和Derived类的虚表创建细节

虚表(vtable)是C++实现动态多态的底层机制,编译器会自动为带虚函数的类生成:

Base类的虚表

  • 当编译器发现Base类有虚函数fun1时,会为Base生成一个独立的虚表。
  • 这个虚表里只包含Base类所有虚函数的地址,这里就是&Base::fun1。
  • 每个Base类对象都会包含一个隐藏的vptr(虚表指针),在Base对象构造时,vptr会被初始化指向Base的vtable。

Derived类的虚表

  • Derived继承自Base,并且重写了fun1,所以编译器会为Derived生成自己的虚表。
  • 生成时,先复制Base虚表的所有条目,然后把被重写的fun1条目替换为&Derived::fun1的地址。
  • Derived对象构造时,先调用Base的构造函数(此时vptr被设为Base的vtable),然后进入Derived的构造函数,把vptr更新为指向Derived自己的vtable。

所以当B指向Derived对象时,虽然B是Base*类型,但对象本身的vptr指向的是Derived的vtable,调用虚函数时自然会找到Derived的fun1。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:01:50