【C/C++】深度探索c++对象模型_笔记

1. 对象内存布局

(1) 普通类(无虚函数)
  • 成员变量排列:按声明顺序存储,但编译器会根据内存对齐规则插入填充字节(padding)。
    class Simple {char a;      // 1字节(偏移0)int b;       // 4字节(偏移4,因对齐跳过1-3字节)double c;    // 8字节(偏移8)
    };
    // 总大小:1 + 3(padding) +4 +8 = 16字节(64位系统)
    
  • 成员函数:独立于对象存储,编译时转换为普通函数,隐式添加 this 指针参数。
(2) 含虚函数的类
  • 虚表指针(vptr):对象头部插入一个指针,指向类的虚函数表(vtable)
  • 虚函数表(vtable):一个函数指针数组,按虚函数声明顺序存储地址。
    class Base {
    public:virtual void func1() {}    // vtable[0]virtual void func2() {}    // vtable[1]int data;                  // 偏移8(假设vptr占8字节)
    };
    
    内存布局[vptr][data]
    vtable内容[&Base::func1, &Base::func2]

2. 虚函数与动态绑定

(1) 多态实现流程
  • 对象构造时:编译器在构造函数中插入代码,将 vptr 指向当前类的虚表。
  • 函数调用时:通过 vptr 找到虚表,再根据函数声明顺序索引到具体函数地址。
    Base* obj = new Derived();
    obj->func1();  // 实际调用 Derived::func1()
    
    底层伪代码
    mov rax, [obj]        ; 获取vptr
    call [rax + 0]        ; 调用vtable[0]处的函数
    
(2) 覆盖与扩展
  • 派生类覆盖虚函数:替换基类虚表中对应的函数指针。
  • 派生类新增虚函数:在虚表末尾追加新条目。
    class Derived : public Base {
    public:void func1() override {}  // 替换Base的vtable[0]virtual void func3() {}   // 追加到vtable[2]
    };
    

3. 继承机制

(1) 单继承
  • 内存布局:基类成员在前,派生类成员在后。
    class Base { int a; };
    class Derived : public Base { int b; };
    // 布局:[Base::a][Derived::b]
    
  • 虚函数表:派生类虚表继承基类虚表条目并覆盖或扩展。
(2) 多重继承
  • 内存布局:按继承顺序排列各基类子对象,每个多态基类有自己的 vptr

    class Base1 { virtual void f1() {} };
    class Base2 { virtual void f2() {} };
    class Derived : public Base1, public Base2 {};
    

    布局[Base1 vptr][Base1 data][Base2 vptr][Base2 data][Derived data]

  • 指针调整:当将 Derived* 转换为 Base2* 时,编译器自动调整指针偏移。

    Derived d;
    Base2* pb2 = &d;  // 指针实际指向 Base2 子对象起始地址
    
(3) 虚继承(解决菱形继承)
  • 虚基类子对象共享:所有虚继承路径共享同一个基类实例。

    class A { int a; };
    class B : virtual public A { int b; };
    class C : virtual public A { int c; };
    class D : public B, public C { int d; };
    

    布局

    • B 部分:[B vptr][B::b][虚基类A的偏移信息]
    • C 部分:[C vptr][C::c][虚基类A的偏移信息]
    • D::d
    • 共享的 A::a(位于对象尾部)
  • 虚基类表(vbtl):存储虚基类子对象的偏移量,供构造函数初始化时使用。


4. 构造函数与析构函数

(1) 构造过程
  • 隐式操作:编译器在构造函数中自动插入以下代码:
    1. 调用基类构造函数。
    2. 初始化 vptr(确保多态正确)。
    3. 初始化虚基类(若存在)。
    4. 执行成员变量的初始化列表。
    5. 执行用户编写的构造函数体。
(2) 虚析构函数
  • 必要性:若基类析构函数非虚,通过基类指针删除派生类对象会导致资源泄漏(派生类析构函数不被调用)。
  • 实现:虚析构函数在虚表中占用一个条目,确保动态绑定到实际对象的析构函数。

5. 函数调用与 this 指针

(1) 成员函数调用
  • 成员函数被编译为普通函数,首个参数为隐式 this 指针。
    // 源代码
    void MyClass::func(int x) { ... }// 编译后伪代码
    void MyClass_func(MyClass* this, int x) { ... }
    
(2) 虚函数调用
  • 通过 vptrvtable 动态解析函数地址,等价于:
    // obj->virtual_func() 的底层行为
    (*(obj->vptr[n]))(obj);  // n为虚函数在表中的索引
    

6. 内存对齐与优化

  • 对齐规则:变量地址通常是其类型大小(sizeof)的整数倍。例如:
    • int(4字节)的地址需是4的倍数。
    • double(8字节)的地址需是8的倍数。
  • 手动调整对齐
    #pragma pack(1)       // 设置1字节对齐(禁用填充)
    struct Unaligned {char a;           // 偏移0int b;            // 偏移1(正常情况下会填充到偏移4)
    };
    #pragma pack()        // 恢复默认对齐
    

7. 模板与异常处理的影响

(1) 模板实例化
  • 每个模板实例化会生成独立的代码,可能导致代码膨胀。例如:
    template<typename T>
    class Box { T data; }; Box<int> a;   // 生成 Box<int> 的代码
    Box<double> b;// 生成 Box<double> 的代码
    
(2) 异常处理
  • 栈展开(Stack Unwinding):抛出异常时,析构局部对象需要依赖虚函数表信息(若涉及多态)。

总结

《深度探索C++对象模型》揭示了C++语法背后的底层实现逻辑,理解这些机制可帮助开发者:

  1. 优化性能:通过内存布局调整减少缓存未命中(Cache Miss)。
  2. 调试复杂问题:如多态失效、内存对齐错误、菱形继承问题。
  3. 避免未定义行为:如错误转换指针导致的内存访问错误。
  4. 设计高效类:权衡虚函数开销与灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905979.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

湖北理元理律师事务所:债务优化中的双维支持实践解析

在债务压力与生活质量失衡的社会议题下&#xff0c;法律服务机构的功能边界正在从单一的法律咨询向复合型支持延伸。湖北理元理律师事务所通过“法律心理”双维服务模式&#xff0c;探索债务优化与生活保障的平衡路径&#xff0c;其方法论或为行业提供实践参考。 法律框架&…

Python uv包管理器使用指南:从入门到精通

Python uv包管理器使用指南&#xff1a;从入门到精通 作为一名Python开发者&#xff0c;你是否曾经为虚拟环境管理和依赖包安装而头疼&#xff1f;今天我要向大家介绍一个强大的工具——uv包管理器&#xff0c;它将彻底改变你的Python开发体验。 什么是uv包管理器&#xff1f…

Windows系统安全加固

掌握的加固点&#xff1a; 用户系统检查 口令策略检查 日志审计检查 安全选项检查 信息保护检查 2.2.1 用户系统检查 #检查系统版本内核 判断依据&#xff1a;无 检查方式&#xff1a;命令 msinfo32 dxdiag查看 #检查Administrator账号是否停用 判断依据&#xff1a;禁…

小蜗牛拨号助手用户使用手册

一、软件简介 小蜗牛拨号助手是一款便捷实用的拨号辅助工具&#xff0c;能自动识别剪贴板中的电话号码&#xff0c;支持快速拨号操作。最小化或关闭窗口后&#xff0c;程序将在系统后台运行&#xff0c;还可设置开机自启&#xff0c;方便随时使用&#xff0c;提升拨号效率。 …

c/c++消息队列库RabbitMQ的使用

RabbitMQ C 消息队列组件设计与实现文档 1. 引言 1.1. RabbitMQ 简介 RabbitMQ 是一个开源的消息代理软件&#xff08;也称为面向消息的中间件&#xff09;&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;。RabbitMQ 服务器是用 Erlang 语言编写的&#…

线程(二)OpenJDK 17 中线程启动的完整流程用C++ 源码详解之主-子线程通信机制

深入解析OpenJDK 17中Java线程的创建与主-子线程通信机制 引言 在Java中&#xff0c;线程的创建与启动通过Thread.start()实现&#xff0c;但底层是JVM与操作系统协作完成的复杂过程。本文基于OpenJDK 17的C源码&#xff0c;揭秘Java线程创建时主线程与子线程的通信机制&…

多线程爬虫语言选择与实现

之前文中有人提到&#xff1a;想要一个简单易用、能快速实现多线程爬虫的方案&#xff0c;而且目标是小网站&#xff0c;基本可以确定对反爬虫措施要求不高&#xff0c;这些就比较简单了。 以往我肯定要考虑常见的编程语言中哪些适合爬虫。Python、JavaScript&#xff08;Node…

AMD Vivado™ 设计套件生成加密比特流和加密密钥

概括 重要提示&#xff1a;有关使用AMD Vivado™ Design Suite 2016.4 及更早版本进行 eFUSE 编程的重要更新&#xff0c;请参阅AMD设计咨询 68832 。 本应用说明介绍了使用AMD Vivado™ 设计套件生成加密比特流和加密密钥&#xff08;高级加密标准伽罗瓦/计数器模式 (AES-GCM)…

Unity3D仿星露谷物语开发44之收集农作物

1、目标 在土地中挖掘后&#xff0c;洒下种子后逐渐成长&#xff0c;然后使用篮子收集成熟后的农作物&#xff0c;工具栏中也会相应地增加该农作物。 2、修改CropStandard的参数 Assets -> Prefabs -> Crop下的CropStandard&#xff0c;修改其Box Collider 2D的Size(Y…

list重点接口及模拟实现

list功能介绍 c中list是使用双向链表实现的一个容器&#xff0c;这个容器可以实现。插入&#xff0c;删除等的操作。与vector相比&#xff0c;vector适合尾插和尾删&#xff08;vector的实现是使用了动态数组的方式。在进行头删和头插的时候后面的数据会进行挪动&#xff0c;时…

CE17.【C++ Cont】练习题组17(堆专题)

目录 1.P2085 最小函数值 题目 分析 方法1:暴力求解 方法2:二次函数的性质(推荐!) 代码 提交结果 2.P1631 序列合并 分析 方法1:建两个堆 第一版代码 提交结果 第二版代码 提交结果 第三版代码 提交结果 方法2:只建一个堆 代码 提交结果 1.P2085 最小函数值…

题单:表达式求值1

题目描述 给定一个只包含 “加法” 和 “乘法” 的算术表达式&#xff0c;请你编程计算表达式的值。 输入格式 输入仅有一行&#xff0c;为需要计算的表达式&#xff0c;表达式中只包含数字、加法运算符 和乘法运算符 *&#xff0c;且没有括号。 所有参与运算的数字不超过…

DeepSeek超大模型的高效训练策略

算力挑战 训练DeepSeek此类千亿乃至万亿级别参数模型,对算力资源提出了极高要求。以DeepSeek-V3为例,其基础模型参数量为67亿,采用专家混合(MoE)架构后实际激活参数可达几百亿。如此规模的模型远超单张GPU显存容量极限,必须借助分布式并行才能加载和训练。具体挑战主要包…

MFC中DoDataExchange的简明指南

基本概念 DoDataExchange 是 MFC 框架中实现数据自动同步的核心函数&#xff0c;主要用于对话框中控件与成员变量的双向绑定。它能让控件中的数据和成员变量自动保持一致&#xff0c;无需手动读写控件数据。 使用示例 1&#xff09;变量声明 在对话框头文件中声明与控件对应…

FreeCAD源码分析: Transaction实现原理

本文阐述FreeCAD中Transaction的实现原理。 注1&#xff1a;限于研究水平&#xff0c;分析难免不当&#xff0c;欢迎批评指正。 注2&#xff1a;文章内容会不定期更新。 一、概念 Ref. from What is a Transaction? A transaction is a group of operations that have the f…

C++类与对象--1 特性一:封装

C面向对象三大特性&#xff1a; &#xff08;1&#xff09;封装&#xff1b;&#xff08;2&#xff09;继承&#xff1b;&#xff08;3&#xff09;多态&#xff1b; C认为万物皆是对象&#xff0c;对象上有对应的属性&#xff08;数据&#xff09;和行为&#xff08;方法&…

初探Reforcement Learning强化学习【QLearning/Sarsa/DQN】

文章目录 一、Q-learning现实理解&#xff1a;举例&#xff1a;回顾&#xff1a; 二、Sarsa和Q-learning的区别 三、Deep Q-NetworkDeep Q-Network是如何工作的&#xff1f;前处理&#xff1a;Convolution NetworksExperience Replay 一、Q-learning 是RL中model-free、value-…

WebRTC技术EasyRTC嵌入式音视频通信SDK打造远程实时视频通话监控巡检解决方案

一、方案概述​ 在现代工业生产、基础设施维护等领域&#xff0c;远程监控与巡检工作至关重要。传统的监控与巡检方式存在效率低、成本高、实时性差等问题。EasyRTC作为一种先进的实时音视频通信技术&#xff0c;具备低延迟、高稳定性、跨平台等特性&#xff0c;能够有效解决这…

专题四:综合练习(括号组合算法深度解析)

以leetcode22题为例 题目分析&#xff1a; 给一个数字n&#xff0c;返回合法的所有的括号组合 算法原理分析&#xff1a; 你可以先考虑如何不重不漏的罗列所有的括号组合 清楚什么是有效的括号组合&#xff1f;&#xff1f;&#xff1f; 1.所有的左括号的数量等于右括号的…

星云智控自定义物联网实时监控模板-为何成为痛点?物联网设备的多样化-优雅草卓伊凡

星云智控自定义物联网实时监控模板-为何成为痛点&#xff1f;物联网设备的多样化-优雅草卓伊凡 引言&#xff1a;物联网监控的模板革命 在万物互联的时代&#xff0c;设备监控已成为保障物联网系统稳定运行的核心环节。传统的标准化监控方案正面临着设备类型爆炸式增长带来的…