深入探索C++对象模型 之 构造,解构,拷贝语意学

深入C++对象模型:构造、拷贝、析构与内存布局全解析 C++对象模型是理解C++底层机制的核心概念,它定义了对象在内存中的布局、生命周期管理及多态实现原理。通过剖析编译器隐式生成的构造函数、拷贝操作与析构函数,开发者可以掌握资源管理、性能优化及避免内存错误的底层逻辑。这一模型的技术价值在于为编写高效、安全的C++代码提供理论基础,尤其在涉及RAII、智能指针及移动语义的现代C++工程实践中至关重要。应用场景包括高性能计算、游戏引擎、系统编程等对内存控制要求严格的领域。本文聚焦于C++对象模型中的构造语意学、拷贝语意学及虚函数表机制,通过分析浅拷贝与深拷贝的陷阱 阅读详情

纯虚拟函数的存在

一个pure virtual function 能被定义和静态地调用,不能经由虚拟机制调用。但是要不要定义又class 设计者决定。唯一例外的情况是pure virtual destructor ,class 设计者一定得定义它,因为一个derived class destructor 会被编译器加以扩展,以静态调用的方式调用其“每一个virtual base class”以及“上一层base class”的destructor。因此,只要缺乏任何一个base class destructors 的定义,就会导致链接失败。C++语言保证:继承体系中每一个class object 的destructors 都会被调用。所以编译器不能够压抑这个调用操作。一个比较好的替代方案就是,不要把virtual destructor 声明为pure 。

 

虚拟规格的存在

当一个函数定义的内容与类型无关时,不应该把他定义成virtual。一般而言,把所有的成员函数都声明为virtual function,然后再靠编译器的优化操作把非必要的virtual invocation 去除,并不是好的设计观念。

 

虚拟规格中const 的存在

虚拟函数最好不要声明成const ,因为你不能保证derived instance 不修改某个data member。

 

“无继承”情况下的对象构造

C中的struct:

在C中,全局变量如果没有明确的初始化操作,被认为是“临时性的定义”,在程序中可以发生多次,最后被链接器折叠起来,只留下单独一个实体,被放在程序 data segment 中一个“特别保留给未初始化的global objects 使用”的空间。又叫做“BSS”(Block Started by Symbol)。而C和C++ 的一个差异就在于,BSS data segment 在C++ 中相对地不重要。C++ 的所有全局对象都被当作“初始化过的数据”来支持。当然,如果一个local object 没有先经过初始化就使用它的初值的话,可能会成为一个潜在的程序臭虫。因为没有声明constructor ,那么用new 定义的object 也不会被初始化。

抽象数据类型(我们定义了一个constructor):

这次global object 将在程序激活时才开始初始化。而local object 和用new 运算符定义的object 变量也将被初始化。

为继承做准备(我们定义了一个constructor和一个virtual function):

这次class object 多了很多负担:vptr,constructor中附加的一些用来初始化vptr的码,合成一个copy constructor 和一个copy assignment operator,而且其操作是nontrivial(有用的)。

当有以传值方式返回对象的函数存在时,提供一个copy constructor 会触发NRV(Named Return Value)优化。

 

继承体系下的对象构造

构造的顺序:

1.       所有的 virtual base class constructors 必须被调用,从左到右,从最深到最浅:

Ø         如果class 被列于member initialization list 中,那么如果有任何明确指定的参数,都应该传递过去。若没有列于list中,而class 有一个default constructor ,也应该调用之。

Ø         此外,class中的每一个virtual base class subobject 的偏移量(offset)必须在执行期可被存取。

Ø         如果class object 是最底层(most-derived)的class,其constructors 可能被调用;某些用以支持这个行为的机制必须被放进来。

2.       所有上一层的base class constructors 必须被调用,以base class 的声明顺序为顺序(与member initialization list 中的顺序没关联):

Ø         如果class 被列于member initialization list 中,那么任何明确指定的参数,都应该传递过去。

Ø         若base class 没有列于member initialization list中,而它有一个default constructor (或default memberwise copy constructor),那么就调用之。

Ø         如果base class 是多重继承下的第二或后继的base class,那么this 指针必须有所调整。

3.       如果class object 有virtual table pointer(s),它(们)必须被设定初值,指向适当的virtual table(s)。

4.       记录在member initialization list 中的data members 初始化操作会被放进constructor 的函数本身,并以members 的声明顺序为顺序。

5.       如果有一个member 并没有出现在member initialization list 之中,但它有一个default constructor,那么该default constructor 必须被调用。

请注意,虽然一个class的destructor 是virtual 的,但是如果它被包含在另一个class中,那么它的调用操作会被静态决议出来。

虚拟继承:

传统的“constructor 扩充现象”并没有用,这是因为virtual base class 的“共享性”之故:

编译器给derived class 的constructor增加有个bool 参数,当是false 时,就不调用virtual base class的constructor。导致只有当一个完整的class object 被定义出来时,它才会被调用;如果object 只是某个完整object 的subobject,它就不会被调用。

Vptr 初始化语意学:

C++ 语言规定:在一个class(base class) 的constructor(和destructor) 中,经由构造中的对象(derived class)来调用一个virtual function,其函数实体应该是在此class(base class) 中有作用的那个。也就是都静态决议,不用到虚拟机制。也就是说,虚拟机制本身必须知道是否这个调用源自一个constructor 之中。而根本的解决之道是,在执行一个constructor 时,必须限制一组virtual functions 候选名单。答案是通过vptr。而vptr 的适当初始化时间是在base class constructors 调用操作之后,但是在程序员供应的码或是“member initialization list 中所列的members初始化操作”之前。因此在class 的constructor 的member initialization list 中调用该class 的一个虚拟函数是安全的,但是在一个class 的member initialization list 中供应参数一个base class constructor 时调用虚拟函数就是不安全的。

 

对象复制语意学

事实上,copy assignment operator 在虚拟继承情况下行为不佳,需要小心地设计和说明。因为 copy assignment operator 缺乏一个member assignment list(也就是平行与member initialization list 的东西),因此编译器没有办法压抑上一层的base class 的copy operators被调用,导致在虚拟继承情况下,derived class 将对virtual base class 进行多重拷贝。C++语言说:我们并灭有规定那些代表virtual base class 的subobjects 是否该被“隐喻定义(implicitly defined)的copy assignment operator”指派(赋值,assign)内容一次以上。因此建议尽可能不要允许一个virtual base class的拷贝操作,甚至可能的话,不要在任何virtual base class 中声明数据。

 

解构语意学

如过class 没有定义destructor,那么只有在class 内带的member object(或是class自己的base class)拥有destructor 的情况下,编译器才会自动合成出一个来。

Destructor 被调用的顺序:

1.       destructor 的函数本身首先被执行。

2.       如果class 用哟member class objects,而后者拥有destructors,那么它们会以其声明顺序的相反顺序被调用。

3.       如果object 内带一个vptr,则现在被重新设定,指向适当之base class 的virtual table。

4.       如果有任何直接的(上一层)nonvirtual base classes 拥有destructor,它们会以声明顺序的相反顺序被调用。

5.       如果有任何virtual base classes 拥有destructor,而当前讨论的这个class 是最尾端(most-derived)的class,那么它们会以其原来的构造顺序的相反顺序被调用。

本地部署 Bolt.new - 小学生都能轻松搭建AI编程环境 阅读详情

相关推荐

PLC模拟量输入电路设计:±10V与±20mA信号调理全解析

在工业自动化和数据采集系统中,模拟量输入是连接物理世界与数字控制的核心桥梁。无论是变送器输出的4-20mA电流环,还是伺服驱动与信号发生器提供的±10V电压信号,PLC和采集模块都需要在复杂现场环境下稳定、精准地完成信号采集。理解信号体制的原理——电流环抗长线干扰、电压环响应快——是设计模拟输入前端的基础。通过精密电阻网络实现分压与采样,利用运放完成双极性到单极性的电平搬移,配合TVS与滤波电路保障可靠性,并经过ADC量化与校准流程,最终构建出兼顾精度、成本和工业级鲁棒性的完整信号链路。本文面向硬件工程师与

weixin_29163797的博客 301

C++对象模型——对象复制语意学 (Object Copy Semantics)(第五章)

5.3    对象复制语意学 (Object Copy Semantics)     当设计一个 class,并以一个 class object指定给 class object时,有三种选择:     1.什么都不做,因此得以实施默认行为.     2.提供一个 explicit copy assignment operator.     3.明确地拒绝一个 class object指定给

yiranant的专栏 986

Voxel Hashing技术解析:如何解决TSDF内存爆炸问题?

本文深入解析Voxel Hashing技术如何有效解决TSDF内存爆炸问题。通过动态内存分配和空间哈希函数设计,该技术将内存占用降低96.6%,同时保持高精度重建。文章详细介绍了GPU加速实现、现代系统演进及实践指南,为实时3D重建提供工程解决方案。

m2n3b4v5c6的博客 715

对象复制语意学(Object Copy Semantics)

当我们设计一个class,并以一个class object赋值给另一个class object时,我们就用到了拷贝复制操作,我们可以选择默认的浅拷贝,也可以自己提供一个explicit copy assignment operator,或者显示的拒绝把一个class object赋值给另一个class object(将copy assignment operator声明为private)。 class Point { public: Point(float x = 0.0,float y = 0.0,fl

ZZHinclude的博客 213

海思Hi3516CV610--SPI NAND Flash移植实战:从手册到驱动的完整适配

本文详细介绍了海思Hi3516CV610芯片上SPI NAND Flash的移植实战,包括从手册解读到驱动适配的完整流程。以GD5F2GM7UEYIGR Flash为例,重点讲解了关键参数确认、模式选择、时序配置以及U-Boot和Linux内核驱动的具体实现步骤,帮助开发者快速完成Flash移植工作。

weixin_29158413的博客 447

Part03 数据结构

本文介绍了CSP-S初赛中常见的高级数据结构及其实现,包括: 线性结构:双端栈、双端队列、单调队列、优先队列和ST表,重点分析了它们的特点、时间复杂度及典型应用场景。 集合与森林:并查集(带路径压缩和按秩合并)和树的孩子兄弟表示法,展示了多叉树转化为二叉树存储的方法。 特殊树结构:包括二叉堆、树状数组、线段树和字典树,详细说明了它们的构建、查询和更新操作,特别强调了线段树的递归实现和字典树的前缀匹配特性。 每种数据结构都配有核心C++代码实现,涉及数组模拟、动态规划和树形结构等编程技巧,适合算法竞赛选手快速

weixin_43863957的博客 1181

NXP eIQ Toolkit 1.2.5深度解析:插件化架构与边缘AI模型部署实战

模型部署是边缘AI应用落地的关键环节,其核心在于将训练好的模型高效、可靠地转换并运行在资源受限的嵌入式设备上。这一过程涉及模型格式转换、算子兼容性、内存优化和计算加速等原理。其技术价值在于打通从算法到硬件的“最后一公里”,直接决定了AI应用在终端设备的性能、功耗和成本。在实际应用场景中,如智能摄像头、工业质检设备等,开发者常面临模型转换失败、量化后精度损失、NPU利用率低等挑战。针对这些痛点,NXP eIQ Toolkit 1.2.5版本带来了重要更新,特别是其**插件化架构**重构了模型转换流程,提升了灵

baochuanying9531的博客 503

01、【solidworks】windosws正在配置solidworks问题及注册表未卸载干净问题解决

windosws正在配置solidworks问题及注册表未卸载干净问题解决

qq_740388770的博客 2万+

【C++对象模型】第五章 构造、解构、拷贝 语意学

1、构造语义学   C++的构造函数可能内带大量的隐藏码,因为编译器会扩充每一个构造函数,扩充程度视 class 的继承体系而定。一般而言编译器所做的扩充操作大约如下: 所有虚基类成员构造函数必须被调用,从左到右,从最深到最浅: 如果该类被列于成员初始化列表中,那么如果有任何明确指定的参数,都应该传递过去。若没有列于列表之中,虚基类的一个默认构造函数被调用(有的话)。 此...

198

从硬件异常到代码救赎:RH850内存访问错误(MAE)的深层诊断与艺术

本文深入探讨瑞萨RH850微控制器的内存访问错误(MAE)诊断与解决方法。针对程序跑飞和Flash Driver交互中的潜在陷阱,提供系统性硬件异常分析框架和防御性编程实践,帮助嵌入式开发者从底层机制到代码层面有效预防和修复MAE问题,提升系统稳定性。

pytorch8learner的博客 197

MacTeX+TeXstudio:Macbook配置本地Latex看这一篇就够了

这个博客记录本人在MacBook上配置本地Latex的流程,其中选择的编译环境为MacTeX,编辑器为TeXstudio。采用的原因主要两点:一个是本人配置的原因主要是用来撰写毕业论文,希望能够尽可能简化设置流程,专注内容表达;第二是由于本人还有一个windows系统的笔记本,想要编辑器在专注写作体验的同时在两个系统上能够都适配,从而减少切换效率,故选择此组合。这篇文章介绍并详细记录了本人的安装流程,有相同需求的同志也可借鉴。

Katherine-XY的博客 6987

杂记——《Inside The C++ Object Model》之四

每一个nonstatic data member的offset在编译期即可获得,即使它属于base class 通过对象和通过对像指针对于data member的存取操作,当data member是一个struct member,class member,单一继承、多重继承的情况完全一样,当data member是一个来自virtual base class的member的时候,则存在差异 ...

hfer的专栏 161

Openai gym environment for multi-agent games

用于多智能体游戏的 OpenAI Gym 环境

suiusoar 1196

深入探索C++对象模型-构造、解构、拷贝、语意学

如果把将一个共同变量加到了接口类中,那么这个类也就是纯虚类了,纯虚类需要定义构造函数来完成这个共同变量的初始化。到底要不要把这个公共变量提取出来下放到接口类中,这一点并无一个标准到底怎么做好。纯虚类中,正常来说那些函数我们都无需定义,除了一个函数之外,纯虚析构函数。由于后续继承的子类在析构的过程中会层层调用上级的析构函数,因此缺乏任何一个base class的destructor的定义,就会导致链接失败。因此,定义析构函数时,不要将其定义为纯虚函数,将其定义为虚函数。

MarksSa的博客 1177

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

深度探索C++对象模型之构造、解构、拷贝语意学小结

1、 一般而言,class的data members应该被初始化,并且只在constructor中或者是在class的member functions中指定初值。其他任何操作都将破坏封装性,使class的维护和修改更加困难。 2、 可以定义并调用invoke一个pure vir

success041000的专栏 1033

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

[读书笔记] - 《深度探索C++对象模型》第5章 构造、解构、拷贝语意学

1.纯虚函数 纯虚函数可以被定义和调用,不过它只能被静态地调用,不能经由虚拟机制调用。 // 定义pure virtual function inline void Abstract_base::interface() const { } inline void Concrete_derived::interface() const { // ok: 静态调用 Abstract_base::interface(); } 要不要定义纯虚函数,全由class设计者决定。但是pure

浮生的博客 361

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

《深度探索C++对象模型》 读书笔记:第五章 构造、解构、拷贝语意学

class Abstract_base { public: virtual ~Abstract_base() = 0; virtual void interface()const = 0; virtual const char* mumble() const{ return _mumble; } protected: char* _mumble; }; 上面这个类的设计存在一个问题,虽...

junya_zhang的博客 312

《深度探索C++对象模型》阅读笔记 第五章 构造、解构、拷贝语意学

const 成员变量的用法和普通 const 变量的用法相似const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值一旦将对象定义为常对象之后,不管是哪种形式,该对象就只能访问被 const 修饰的成员了(包括 const 成员变量和 const 成员函数),因为非 const 成员可能会修改对象的数据(编译器也会这样假设),C++禁止这样做。1. 即使是一个抽象基类,如果它有非静态数据成员,也应该给它提供一个带参数的构造函数,来初始化它的数据成员。或许你可以通过其派生类来初始化它的数据成员(假

哇咔咔的博客 346

《深度探索C++对象模型》读书笔记

 Inside The C++ Object ModelStanley B. Lippman 屋檐下的水滴--读书笔记系列http://blog.youkuaiyun.com/dwater 《深度探索C++对象模型》读书笔记                                                                          

DWATER 6126

《深度探索C++对象模型》读书笔记第五章:构造、解构、拷贝语意学

纯虚基类尽量不要定义数据成员,如果定义了就需要在构造函数或其他成员函数设定初值,不过这通常是一种不好的设计。纯虚基类的纯虚函数可以在派生类中以静态方式调用。声明了纯虚析构函数就一定得定义它,为什么?因为每一个派生类析构函数都会被编译器扩展,以静态调用方式调用其每一个虚基类以及上一层基类的析构函数。因此,只要缺乏任何一个基类析构函数的定义,就会导致链接失败。如果某个函数其函数定义内容不与类型有关,不

FreeeLinux's blog 1005

深度探索C++对象模型 学习笔记 第五章 构造、解构、拷贝语意学(2)

更一般地说,在某个类(这里即 Point3d 类)的构造函数(以及析构函数)内部,正在构造的对象(这里是 PVertex 对象)对虚函数的调用,被限定在该类(即 Point3d 类)内部活动的虚函数版本上。禁止拷贝的方法是:将拷贝赋值运算符声明为 private,并且不提供定义(声明为私有后,除了类的成员函数和友元之外,其他地方都无法进行赋值,而我们又不提供定义,这样一来,如果有成员函数或友元试图进行拷贝,程序就会链接失败,诚然,依赖链接器的特性——也就是依赖语言外部的东西——并不完全令人满意)。

tus00000的博客 269

深度探索C++对象模型 学习笔记 第五章 构造、解构、拷贝语意学(1)

遗憾的是,我手头没有实际采用这种方案的编译器,因此无法提供数据来证实这一点(不过,在 Foundation 项目期间,Rob Murray——我猜他是出于无奈——手动优化了 cfront 生成的 C 代码,消除了不必要的条件测试和虚表指针设置。而local中的三个成员值会是随机值,因为默认构造函数中没有为三个成员设置初始值,即使是我们显式提供的构造函数,如果没有为成员设置初始值,成员的初始值也是随机的。但在实际实现中,这些平凡成员既不会被定义,也不会被调用,程序的行为和 C 语言下完全一样。

tus00000的博客 391

《深度探索C++对象模型》笔记目录

读书笔记,特此记录。 😇😇😇 《深度探索C++对象模型》阅读笔记 第一章 关于对象 《深度探索C++对象模型》阅读笔记 第二章 构造函数语意学 《深度探索C++对象模型》阅读笔记 第三章 Data语意学 《深度探索C++对象模型》阅读笔记 第四章 Function语意学 《深度探索C++对象模型》阅读笔记 第五章 构造、解构、拷贝语意学 《深度探索C++对象模型》阅读笔记 第六章 执行期语意学 《深度探索C++对象模型》阅读笔记 第七章 站在对象模型的尖端...

哇咔咔的博客 364

《深度探索C++对象模型》勘误表

《深度探索C++对象模型》(侯捷译),英文原版书名为《Inside the C++ Object Model》,Stanley B.Lippman著。光看作者和译者的名字,就知道这本书非同反响。的确,无论是这本书的原版还是译作,都被读者给予了很高的评价。我,作为一名C++的爱好者,当然是怀着一份无比景仰的心情,仔细阅读完了全书。现在将书中我觉得需要勘误的地方摘出来,与大家共享。1.第五章“构造

mlite的专栏 2252

深度探索C++对象模型

深度探索C++对象模型参考链接:第1章 关于对象C++的额外成本三种对象模型简单对象模型表格驱动对象模型C++对象模型class和struct关键字的差异三种编程典范一个类的对象的内存大小一些结论[^3] 参考链接: 深度探索C++对象模型 笔记汇总 第1章 关于对象 C++的额外成本 C++较之C的最大区别,无疑在于面向对象。类相较于C的struct不仅只包含了数据,同时还包括了对于数...

zzxiaozhao的博客 3752

深度探索C++对象模型pdf

下载地址:网盘下载内容简介······这本书探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序范例、图片,以及对象导向观念和底层对象模型之间的效率测量。举报作者简介······Stanley B...

weixin_30301449的博客 1331
上一篇: 设计模式之我爱我家
下一篇: 深入探索C++对象模型 之 执行期语意学
C_plus_learner
博客等级 码龄22年 0粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值