跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 多态底层实现机制深度解析

综述由AI生成C++ 多态通过虚函数表指针(vptr)和虚函数表(vtable)在运行时实现动态绑定。基类对象包含指向虚表的指针,派生类重写虚函数时覆盖表中对应地址。静态绑定发生在编译期,动态绑定发生在运行期,通过查找虚表确定实际调用函数。虚表通常存储在代码段常量区,对象内存布局中增加了一个指针大小用于存储 vptr。

FrontendX发布于 2026/3/22更新于 2026/5/1211 浏览
C++ 多态底层实现机制深度解析

C++ 多态底层实现机制深度解析

在之前的讨论中,我们初步了解了虚函数、重写、纯虚函数等语法层面的知识。但很多同学在深入理解时仍会有疑问:为什么带虚函数的类 sizeof 会变大?基类指针指向不同派生类对象时,运行时如何找到对应函数?虚表、虚指针到底存在内存的哪个区域?

本文将从内存布局、对象模型及汇编视角出发,彻底讲透 C++ 多态的底层原理。

虚函数与普通函数的区别

我们可以通过一个简单的例子来观察内存差异。

class Base {
public:
    virtual void Func1() { cout << "Func1()" << endl; }
protected:
    int _b = 1;
    char _ch = 'x';
};

int main() {
    Base b;
    cout << sizeof(b) << endl;
    return 0;
}

对于普通类,成员函数不占用对象内存空间。理论上 _b (4 字节) + _ch (1 字节) 经过内存对齐后应为 8 字节。但实际运行结果是 12 字节。

这是因为编译器在含有虚函数的类对象中隐藏添加了一个指针,称为虚函数表指针(vptr)。一个含有虚函数的类至少包含一个 vptr,它指向该类的虚函数表(vtable)。

内存布局示意图

多态的实现原理

动态绑定与静态绑定

多态的核心在于动态绑定。当满足多态条件(指针或引用 + 调用虚函数)时,编译器不会在编译期确定函数地址,而是生成代码让程序在运行时通过对象的 vptr 找到对应的虚表,再从虚表中获取函数地址进行调用。

如果不满足多态条件(如直接调用非虚函数),则发生静态绑定,编译器直接在编译期确定函数地址。

class Person {
public:
    virtual void BuyTicket() { cout << "买票 - 全价" << endl; }
private:
    string _name;
};

class Student : public Person {
public:
    virtual void BuyTicket() { cout << "买票 - 打折" << endl; }
private:
    string _id;
};

class Soldier : public Person {
public:
    virtual void BuyTicket() { cout << "买票 - 优先" << endl; }
private:
    string _codename;
};

void Func(Person* ptr) {
    // 虽然都是 Person 指针,但具体调用哪个函数取决于 ptr 指向的对象类型
    ptr->BuyTicket();
}

int main() {
    Person ps;
    Student st;
    Soldier sr;
    Func(&ps);
    Func(&st);
    Func(&sr);
    return 0;
}

从汇编层面看,动态绑定涉及间接寻址。例如 ptr->BuyTicket() 会被编译为类似以下的指令序列:先取出指针,再取出指针指向的虚表地址,最后跳转到虚表中记录的函数地址。

mov eax, dword ptr [ptr]
mov edx, dword ptr [eax]
call dword ptr [edx]

相比之下,静态绑定则是直接调用固定地址。

call Student::Student

虚函数表结构

  1. 虚表内容:基类对象的虚函数表中存放基类所有虚函数的地址。同类型的对象共用同一张虚表,不同类型的对象各自有独立的虚表。
  2. 继承覆盖:派生类重写基类虚函数时,派生类虚表中对应的条目会被覆盖为派生类重写的函数地址。派生类虚表通常包含:(1) 基类未重写的虚函数地址,(2) 派生类重写的虚函数地址,(3) 派生类新增的虚函数地址。
  3. 虚表位置:虚函数本身是代码段中的指令,而虚函数表(存储函数指针的数组)通常位于代码段的常量区(Read-Only Data Segment)。
  4. 结束标记:部分编译器(如 VS)会在虚表末尾放置 0x00000000 作为结束标记,但这并非 C++ 标准规定,g++ 等编译器可能不包含此标记。

我们可以通过打印地址来验证虚表的存储位置。

class Base {
public:
    virtual void func1() { cout << "Base::func1" << endl; }
    virtual void func2() { cout << "Base::func2" << endl; }
    void func5() { cout << "Base::func5" << endl; }
protected:
    int a = 1;
};

class Derive : public Base {
public:
    virtual void func1() { cout << "Derive::func1" << endl; }
    virtual void func3() { cout << "Derive::func3" << endl; }
    void func4() { cout << "Derive::func4" << endl; }
protected:
    int b = 2;
};

int main() {
    Base b;
    Derive d;
    Base* p3 = &b;
    Derive* p4 = &d;

    printf("栈:%p\n", &b);
    printf("Person 虚表地址:%p\n", *(int*)p3);
    printf("Student 虚表地址:%p\n", *(int*)p4);
    printf("虚函数地址:%p\n", &Base::func1);
    printf("普通函数地址:%p\n", &Base::func5);
    return 0;
}

运行结果通常会显示虚表地址与普通函数地址都在代码段区域,且虚表地址独立于对象实例。

虚表位置验证

通过上述分析可以看出,多态并非简单的语法糖,而是一套完整的运行时机制。理解 vptr 和 vtable 的交互,有助于我们在编写高性能 C++ 代码时做出更合理的内存与性能权衡。

目录

  1. C++ 多态底层实现机制深度解析
  2. 虚函数与普通函数的区别
  3. 多态的实现原理
  4. 动态绑定与静态绑定
  5. 虚函数表结构
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 智慧社区可视化平台的设计与实现
  • OpenClaw 作者观点:CLI 才是 AI 连接世界的终极接口
  • C++ 测试与调试实战:保障代码质量与稳定性
  • 零基础学习编程语言的通用方法与进阶路径
  • Qwen2 命名实体识别微调实战指南
  • 期货交易基础:保证金制度与每日结算机制详解
  • AI Agent 赋能新闻媒体:自动写作与内容分发实践
  • 医疗自然语言处理(NLP)实战:从场景到模型落地
  • 前端 AI 与营销增长领域的 AI 应用核心趋势
  • 机器人操作 VLA 模型的强化学习综述
  • 基于 DeepSeek 和 Cursor 打造智能代码审查工具
  • Git 入门:配置、核心概念与文件操作
  • 单链表应用:经典算法题与通讯录实现
  • Java 安全实践:SSL 与 MD5 双重加密在数据库交互中的应用
  • Java 中 ML-KEM 密钥封装机制的 5 大核心实现步骤
  • Google Gemini 图像生成模型实测:Nano Banana Pro 免费使用指南
  • 前端国际化实战:让应用支持多语言
  • GitHub 十大 Claude Skills 精选与实战配置指南
  • Ubuntu ISO 镜像下载地址汇总
  • 揭秘黑客:成为网络安全工程师的必备技能清单

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online