news 2026/4/15 5:52:07

C++继承中的虚函数机制:从单继承到多继承的深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++继承中的虚函数机制:从单继承到多继承的深度解析
虚函数表(vtable)

每个包含虚函数(或继承自含虚函数的类)的类,都会在编译阶段生成一个唯一的虚函数表。它本质是一个函数指针数组,但并非仅包含函数地址——主流实现中,vtable通常以type_info指针(用于dynamic_casttypeid的RTTI信息)开头,随后才是虚函数指针列表。例如,一个简单类的vtable结构可能如下:

代码语言:txt

AI代码解释

Base vtable: [0] type_info* for Base // RTTI信息 [1] &Base::func1 // 虚函数指针 [2] &Base::func2 // 虚函数指针

vtable的关键特性包括:

  • 类级唯一性:每个类(含派生类)有且仅有一个vtable,所有对象共享该表;
  • 编译期生成:编译器在编译阶段确定vtable大小及内容,运行时只读;
  • 继承关联性:派生类vtable与基类vtable存在结构性关联,是实现多态的基础。
虚函数指针(vptr)

每个包含虚函数的对象,都会隐含一个虚函数指针(vptr),用于指向其所属类的vtable。vptr的初始化与维护由编译器自动完成:

  • 初始化时机:对象构造过程中,在基类构造函数执行前(或执行中,依编译器实现),vptr被设置为指向当前类的vtable;
  • 存储位置:通常位于对象内存布局的起始位置(如64位系统中,对象首8字节为vptr),但不同编译器可能有差异(如存在虚基类时位置可能调整);
  • 隐藏性:vptr是编译器自动添加的隐藏成员,无法在用户代码中直接访问,但可通过调试工具或指针操作间接观察。

例如,一个包含vptr的对象内存布局(64位系统)通常为:

代码语言:txt

AI代码解释

+----------------+ // 起始地址 | vptr (8字节) | // 指向类的vtable +----------------+ | 成员变量1 | // 用户定义的成员 +----------------+ | 成员变量2 | +----------------+

单继承中的虚函数机制

单继承是最常见的继承场景,其虚函数机制相对简单:派生类通过扩展基类的vtable,并替换重写的虚函数地址,实现多态。

基本结构与内存布局

以如下代码为例:

代码语言:cpp

AI代码解释

class Base { public: virtual void func1() {} // 虚函数1 virtual void func2() {} // 虚函数2 int base_data; // 数据成员 }; class Derived : public Base { public: void func1() override {} // 重写func1 virtual void func3() {} // 新增虚函数 int derived_data; // 派生类数据成员 };

Base对象内存布局(64位系统,假设对齐为8字节):

  • 首8字节:vptr(指向Base vtable);
  • 接下来4字节:base_data(int类型);
  • 填充4字节(满足8字节对齐);
  • 总大小:16字节(8+4+4)。

Derived对象内存布局

  • 继承Base的所有成员(vptr、base_data、填充);
  • 新增的derived_data(4字节);
  • 填充4字节(对齐);
  • 总大小:24字节(16+4+4)。

布局图示如下:

代码语言:txt

AI代码解释

Base对象: +----------------+ | vptr → Base vtable | // 8字节 +----------------+ | base_data | // 4字节 +----------------+ | (填充) | // 4字节(对齐到8字节) +----------------+ Derived对象: +----------------+ | vptr → Derived vtable | // 8字节(覆盖Base的vptr) +----------------+ | base_data | // 4字节(继承自Base) +----------------+ | (填充) | // 4字节(Base部分对齐) +----------------+ | derived_data | // 4字节(新增成员) +----------------+ | (填充) | // 4字节(整体对齐到8字节) +----------------+
虚函数表的演变

vtable是单继承中多态实现的核心。派生类vtable并非独立创建,而是以基类vtable为基础扩展

Base vtable结构

Base类的vtable包含RTTI信息和其声明的虚函数:

代码语言:txt

AI代码解释

Base vtable: [0] type_info* for Base // RTTI指针(用于typeid/ dynamic_cast) [1] &Base::func1 // 虚函数地址 [2] &Base::func2 // 虚函数地址
Derived vtable结构

Derived类继承Base后,vtable发生如下变化:

  1. 重写的虚函数替换:Derived::func1覆盖Base::func1,占据原索引位置;
  2. 继承的虚函数保留:Base::func2未被重写,地址保持不变;
  3. 新增虚函数追加:Derived::func3添加到vtable末尾。

因此,Derived vtable结构为:

代码语言:txt

AI代码解释

Derived vtable: [0] type_info* for Derived // RTTI指针(更新为Derived类型) [1] &Derived::func1 // 重写:替换原Base::func1 [2] &Base::func2 // 继承:保持原地址 [3] &Derived::func3 // 新增:追加到末尾
单继承虚函数调用流程

当通过基类指针调用虚函数时,多态通过vptr和vtable实现:

代码语言:cpp

AI代码解释

Base* ptr = new Derived(); // Base指针指向Derived对象 ptr->func1(); // 调用Derived::func1而非Base::func1

底层流程解析:

  1. 获取vptr:从ptr指向的对象首地址读取vptr(即Derived vtable的地址);
  2. 索引vtable:根据func1在vtable中的固定索引(示例中为索引1),获取函数指针;
  3. 调用函数:通过函数指针执行Derived::func1。

这一过程的关键在于索引位置固定:派生类不会改变继承的虚函数在vtable中的索引,确保基类指针能正确定位到派生类重写的函数。

单继承虚函数机制的特点
  1. 单一vptr:整个继承链中仅需一个vptr,所有虚函数通过该指针访问;
  2. vtable扩展式增长:派生类vtable是基类vtable的“超集”,结构清晰;
  3. 高效调用:虚函数调用仅需一次vptr解引用+固定索引访问,性能接近直接函数调用;
  4. 内存开销可控:对象仅增加一个vptr(8字节),vtable为类级共享,不占用对象内存。

多继承中的虚函数机制

多继承(一个派生类继承多个基类)显著增加了虚函数机制的复杂性。当多个基类均包含虚函数时,派生类需同时维护多个vtable和vptr,且需解决this指针调整等问题。

基本结构与内存布局

以双重继承为例:

代码语言:cpp

AI代码解释

class Base1 { public: virtual void func1() {} // 虚函数 virtual void func2() {} // 虚函数 int base1_data; // 数据成员 }; class Base2 { public: virtual void func3() {} // 虚函数 virtual void func4() {} // 虚函数 int base2_data; // 数据成员 }; class Derived : public Base1, public Base2 { // 多继承Base1和Base2 public: void func1() override {} // 重写Base1::func1 void func4() override {} // 重写Base2::func4 virtual void func5() {} // 新增虚函数 int derived_data; // 派生类数据成员 };

Derived对象内存布局(64位系统,对齐8字节):

  • 首先包含完整的Base1子对象(vptr1 + base1_data + 填充);
  • 随后包含完整的Base2子对象(vptr2 + base2_data + 填充);
  • 最后是Derived自身的数据成员derived_data及填充。

布局图示如下:

代码语言:txt

AI代码解释

Derived对象: +-------------------+ // Base1子对象开始 | vptr1 → Derived vtable (Base1部分) | // 8字节(Base1的vptr) +-------------------+ | base1_data | // 4字节(Base1数据) +-------------------+ | (填充) | // 4字节(Base1对齐到8字节) +-------------------+ // Base1子对象结束(总16字节) | vptr2 → Derived vtable (Base2部分) | // 8字节(Base2的vptr) +-------------------+ | base2_data | // 4字节(Base2数据) +-------------------+ | (填充) | // 4字节(Base2对齐到8字节) +-------------------+ // Base2子对象结束(总16字节,累计32字节) | derived_data | // 4字节(Derived数据) +-------------------+ | (填充) | // 4字节(整体对齐到8字节) +-------------------+ // 总大小:40字节(32+4+4)

关键差异:多继承下对象包含多个vptr(每个有虚函数的基类贡献一个),分别对应不同基类的vtable视图。

多继承中的多个vtable

Derived类需要为每个基类维护独立的vtable“视图”,以确保不同基类指针能正确访问虚函数。

Base1部分的vtable

Base1作为第一个基类,其vtable视图包含:

  • 重写的Base1::func1;
  • 继承的Base1::func2;
  • 新增的Derived::func5(追加到末尾)。

结构如下:

代码语言:txt

AI代码解释

Derived vtable (Base1视图): [0] type_info* for Derived // RTTI指针 [1] &Derived::func1 // 重写Base1::func1 [2] &Base1::func2 // 继承Base1::func2 [3] &Derived::func5 // 新增Derived::func5
Base2部分的vtable

Base2作为第二个基类,其vtable视图需解决两个问题:

  1. 重写的Base2::func4需关联到Derived实现;
  2. 确保通过Base2指针调用时,this指针正确指向Derived对象。

因此,Base2视图的vtable结构为:

代码语言:txt

AI代码解释

Derived vtable (Base2视图): [0] type_info* for Derived // RTTI指针(与Base1视图共享) [1] &Base2::func3 // 继承Base2::func3 [2] &thunk_Derived::func4 // 重写Base2::func4(通过thunk函数)
Thunk函数:解决this指针调整问题

多继承中最复杂的问题是this指针偏移。当通过Base2指针访问Derived对象时,该指针实际指向Derived对象中Base2子对象的起始位置(示例中为偏移16字节处),而非整个对象的起始位置。若直接调用Derived::func4,this指针将指向Base2子对象,导致访问Derived成员时地址错误。

Thunk函数(跳板函数)通过调整this指针解决这一问题。它是编译器生成的小辅助函数,执行以下操作:

  1. 将Base2指针调整为完整的Derived指针(减去Base2子对象在Derived中的偏移量);
  2. 跳转到实际的Derived::func4执行。

www.dongchedi.com/article/7598265380330848830
www.dongchedi.com/article/7598273072999186968
www.dongchedi.com/article/7598274901841379865
www.dongchedi.com/article/7598276424889336345
www.dongchedi.com/article/7598275637006664254
www.dongchedi.com/article/7598279236490723865
www.dongchedi.com/article/7598274890558800409
www.dongchedi.com/article/7598282614747251224
www.dongchedi.com/article/7598276969335669310
www.dongchedi.com/article/7598278977895121470
www.dongchedi.com/article/7598282709198717464
www.dongchedi.com/article/7598281407030936089
www.dongchedi.com/article/7598277471599346201
www.dongchedi.com/article/7598283447504896536
www.dongchedi.com/article/7598287089956192793
www.dongchedi.com/article/7598287774483202584
www.dongchedi.com/article/7598288632398856728
www.dongchedi.com/article/7598287158570435097
www.dongchedi.com/article/7598292330680418878
www.dongchedi.com/article/7598288380383855129
www.dongchedi.com/article/7598290710689219134
www.dongchedi.com/article/7598288042881073689
www.dongchedi.com/article/7598291946742317593
www.dongchedi.com/article/7598293637713478206
www.dongchedi.com/article/7598290400063373848
www.dongchedi.com/article/7598292454667944472
www.dongchedi.com/article/7598291773202776638

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/10 20:13:11

亲自动手试了verl,结果出乎意料地顺利!

亲自动手试了verl,结果出乎意料地顺利! 最近一直在研究如何高效地对大语言模型进行强化学习后训练,尤其是在数学推理这类需要多步逻辑的任务上。之前尝试过一些开源框架,要么配置复杂、依赖冲突,要么运行效率低得让人…

作者头像 李华
网站建设 2026/4/13 18:54:57

从零开始:用Sambert镜像搭建中文语音合成Web服务

从零开始:用Sambert镜像搭建中文语音合成Web服务 1. 为什么你需要一个开箱即用的中文TTS服务? 你有没有遇到过这样的场景: 做一个本地知识库项目,想给每篇文章配上语音朗读,但试了三个开源TTS模型,两个卡…

作者头像 李华
网站建设 2026/4/11 7:30:00

智能客服实战:用GLM-ASR-Nano-2512快速搭建语音问答系统

智能客服实战:用GLM-ASR-Nano-2512快速搭建语音问答系统 在智能客服场景中,语音识别是连接用户与服务系统的“第一道门”。传统方案往往依赖云端API,存在延迟高、隐私风险大、成本不可控等问题。而今天我们要介绍的 GLM-ASR-Nano-2512&#…

作者头像 李华
网站建设 2026/4/9 18:28:19

fft npainting lama大图处理策略:2000px以上图像优化方案

FFT NPainting LaMa大图处理策略:2000px以上图像优化方案 1. 为什么大图修复总卡顿、出错或效果差? 你有没有试过用LaMa模型修复一张30004000的电商主图,结果等了两分钟只弹出“CUDA out of memory”?或者修复完边缘发灰、纹理断…

作者头像 李华
网站建设 2026/4/4 4:52:25

模型乱码怎么办?Open-AutoGLM常见问题解决方案

模型乱码怎么办&#xff1f;Open-AutoGLM常见问题解决方案 1. 问题定位&#xff1a;什么是“模型乱码”&#xff1f; 在使用 Open-AutoGLM 过程中&#xff0c;你可能遇到这样的情况&#xff1a; 输入指令后&#xff0c;AI 返回一串无法识别的符号&#xff0c;比如 、<0x…

作者头像 李华
网站建设 2026/4/13 6:59:01

iOS系统增强工具TrollRestore零基础上手教程

iOS系统增强工具TrollRestore零基础上手教程 【免费下载链接】TrollRestore TrollStore installer for iOS 17.0 项目地址: https://gitcode.com/gh_mirrors/tr/TrollRestore 如何在3分钟内完成iOS系统增强&#xff1f;对于许多iOS用户而言&#xff0c;系统的封闭性常常…

作者头像 李华