跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++ 多态底层实现原理详解

C++ 多态通过虚函数表指针和虚函数表机制实现。含有虚函数的类对象内部包含虚表指针,指向存放虚函数地址的数组。派生类重写虚函数时会覆盖虚表中对应地址。运行时根据对象实际类型在虚表中查找函数地址实现动态绑定,否则为编译时静态绑定。虚表通常存储在代码段的常量区。

随缘发布于 2026/3/27更新于 2026/10/2488 浏览
C++ 多态底层实现原理详解

前言

在多态核心语法层面(虚函数、重写、纯虚函数等)的基础上,多态的底层依赖于 vptr 虚指针与 vtable 虚函数表。学习时常有以下疑问:带虚函数的类 sizeof 为何多出字节?基类指针指向不同派生类对象如何找到对应函数?虚表、虚指针存在内存哪个区域?静态绑定和动态绑定的区别?本篇从内存布局、对象模型、汇编视角出发,彻底讲透 C++ 多态底层原理。

正文

一、虚函数和普通函数的区别

对于普通类,成员函数所占内存总和在对齐后即为类内存大小。但带有虚函数的类,sizeof 会多出 4/8 字节(取决于系统位数)。这是因为对象中包含了一个虚函数表指针(vptr)。一个含有虚函数的类至少都有一个虚函数表指针,所有虚函数的地址被放到该类的虚函数表(vtable)中。

二、多态的原理

多态是如何实现的

满足多态条件后,底层不再是编译时通过调用对象确定函数地址,而是运行时到指向对象的虚表中确定对应的虚函数地址。这样实现了指针或引用指向基类就调用基类的虚函数,指向派生类就调用派生类对应的虚函数。

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 在调用 BuyTicket
    // 但是跟 ptr 没关系,而由 ptr 指向的对象决定的。
    ptr->();
}
{
    Person ps;
    Student st;
    Soldier sr;
    (&ps);
    (&st);
    (&sr);
     ;
}
BuyTicket
int main()
Func
Func
Func
return
0
动态绑定与静态绑定
  • 对不满足多态条件(指针或者引用 + 调用虚函数)的函数调用是在编译时绑定,即编译时确定调用函数的地址,叫做静态绑定。
  • 满足多态条件的函数调用是在运行时绑定,即在运行时到指向对象的虚函数表中找到调用函数的地址,也就做动态绑定。

动态绑定示例(汇编视角):

; ptr 是指针 + BuyTicket 是虚函数满足多态条件。
; 这里是动态绑定,编译在运行时到 ptr 指向对象的虚函数表中确定调用函数地址
mov eax, dword ptr [ptr]
mov edx, dword ptr [eax]
mov esi, esp
mov ecx, dword ptr [ptr]
mov eax, dword ptr [edx]
call eax

静态绑定示例(汇编视角):

; BuyTicket 不是虚函数,不满足多态条件。
; 这里是静态绑定,编译器直接确定调用函数地址
mov ecx, dword ptr [ptr]
call Student::Student
虚函数表
  • 基类对象的虚函数表中存放基类所有虚函数的地址。同类型的对象共用同一张虚表,不同类型的对象各自有独立的虚表,所以基类和派生类有各自独立的虚表。
  • 派生类由两部分构成,继承下来的基类和自己的成员。一般情况下,继承下来的基类中有虚函数表指针,自己就不会再生成虚函数表指针。但要注意这里继承下来的基类部分虚函数表指针和基类对象的虚函数表指针不是同一个。
  • 派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址。
  • 派生类的虚函数表中包含:(1) 基类的虚函数地址,(2) 派生类重写的虚函数地址完成覆盖,(3) 派生类自己的虚函数地址三个部分。
  • 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个 0x00000000 标记(C++ 标准未规定,vs 系列编译器会放,g++ 系列不会)。
  • 虚函数存在哪?虚函数和普通函数一样,编译好后是一段指令,都存在代码段,只是虚函数的地址又存到了虚表中。
  • 虚函数表存在哪?C++ 标准并未规定,但在 vs 下是存在代码段(常量区)。

验证代码:

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::func1" << endl; }
    void func4() { cout << "Derive::func4" << endl; }
protected:
    int b = 2;
};
int main() {
    Base b;
    Derive d;
    Base* p3 = &b;
    Derive* p4 = &d;
    printf("Base 虚表地址:%p\n", *(int*)p3);
    printf("Derive 虚表地址:%p\n", *(int*)p4);
    printf("虚函数地址:%p\n", &Base::func1);
    printf("普通函数地址:%p\n", &Base::func5);
    return 0;
}

运行结果分析显示,虚表地址在常量区。

目录

  1. 前言
  2. 正文
  3. 一、虚函数和普通函数的区别
  4. 二、多态的原理
  5. 多态是如何实现的
  6. 动态绑定与静态绑定
  7. 虚函数表

更多推荐文章

查看全部
  • DeepSeek 中冷启动数据与多阶段训练的作用
  • OpenClaw 对接 Stable Diffusion 实现免费 AI 绘画
  • 网络安全领域主流认证体系解析与选择指南
  • 数据结构(清华大学)严蔚敏版教材
  • DeepSeek 隐藏玩法与提示词技巧指南
  • Stable Diffusion 显存管理技术解析与优化实践
  • HTML5 Web Workers 详解:利用后台线程提升网页性能
  • Python 11 种常用编译器与解释器介绍
  • 使用 Dify 搭建企业知识库聊天机器人
  • Linux 线程同步与互斥详解(含 C++ 代码示例)
  • Kali Linux 2025.4 正式发布:告别 X11、Wayland 全适配、工具与镜像升级
  • Spring Boot 统一异常处理:实现、原理与最佳实践
  • JVM 内存模型详解:运行时数据区结构解析
  • Java AWT 布局管理器详解
  • Java 9 至 Java 25 语言演进与技术革新分析
  • Llama-2-7b 在昇腾 NPU 上的六大核心场景性能基准
  • 使用 LLaMA-Factory 训练 Qwen2.5-VL 进行目标检测
  • 基于遗传算法与 RBF 神经网络的无人机故障容错控制仿真
  • Spring 配置类与注解方式详解
  • Pygame 游戏开发完整流程与实战解析

相关免费在线工具

  • 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

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online