[翻译] Effective C++, 3rd Edition, Item 14: 谨慎考虑 resource-managing classes(资源管理类)中的拷贝行为

DLPFC.h5ad格式数据简便获取方式+STAGATE论文复现问题 简便获取DLPFC.h5ad格式的数据方式 阅读详情

Item 14: 谨慎考虑 resource-managing classes(资源管理类)中的拷贝行为

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.youkuaiyun.com/fatalerror99/

Item 13 介绍了作为 resource-managing classes(资源管理类)支柱的 Resource Acquisition Is Initialization (RAII) 原则,并描述了 auto_ptr 和 tr1::shared_ptr 在 heap-based(基于堆)的资源上运用这一原则的表现。然而,并非所有的资源都是 heap-based(基于堆)的,而对于这样的资源,像 auto_ptr 和 tr1::shared_ptr 这样的 smart pointers(智能指针)通常就不像 resource handlers(资源句柄)那样合适。在这种情况下,有时,你很可能要根据你自己的需要去创建你自己的 resource-managing classes(资源管理类)。

例如,假设你使用一个 C API 提供的 lock 和 unlock 函数去操纵 Mutex 类型的 mutex objects(互斥体对象):

void lock(Mutex *pm);               // lock mutex pointed to by pm

void unlock(Mutex *pm);             // unlock the mutex

为了确保你从不会忘记解锁一个被你加了锁的 Mutex,你案打算创建一个 class 来管理锁。这样一个 class 的基本结构被 RAII 原则规定,通过 construction(构造函数)获取资源并通过 destruction(析构函数)释放它:

class Lock {
public:
  explicit Lock(Mutex *pm)
  : mutexPtr(pm)
  { lock(mutexPtr); }                          // acquire resource

  ~Lock() { unlock(mutexPtr); }                // release resource

private:
  Mutex *mutexPtr;
};

客户按照惯常的 RAII 风格来使用 Lock:

Mutex m;                    // define the mutex you need to use
...
{                           // create block to define critical section
 Lock ml(&m);               // lock the mutex
...                         // perform critical section operations

}                           // automatically unlock mutex at end
                            // of block

这没什么问题,但是如果一个 Lock object 被拷贝应该发生什么?

Lock ml1(&m);                      // lock m

Lock ml2(ml1);                     // copy ml1 to ml2—what should
                                   // happen here?

这是一个更一般的问题的特定实例,每一个 RAII class 的作者都必须面对它:当一个 RAII object 被拷贝的时候应该发生什么?大多数情况下,你需要从下面各种可能性中挑选一个:

  • prohibit copying(禁止拷贝)。在很多情况下,允许 RAII objects 被拷贝是没有意义的。这对于像 Lock 这样 class 很可能是正确的,因为同步原本的“拷贝”很少有什么意义。当拷贝对一个 RAII class 没有什么意义的时候,你应该禁止它。Item 6 解释了如何做到这一点:声明拷贝操作为私有。对于 Lock,看起来可能就像这样:

class Lock: private Uncopyable {            // prohibit copying — see
public:                                     //
Item 6
 ...                                        // as before
};

  • reference-count the underlying resource(对底层的资源引用计数)。有时人们需要的是保持一个资源直到最后一个使用它的 object 被销毁。在这种情况下,拷贝一个 RAII object 应该增加引用这一资源的 objects 的数目。这也就是被 tr1::shared_ptr 使用的 "copy"(“拷贝”)的含意。

通常,RAII classes 只需要包含一个 tr1::shared_ptr data member(数据成员)就能够实现 reference-counting(引用计数)的拷贝行为。例如,如果 Lock 要使用 reference counting(引用计数),他可能要将 mutexPtr 的类型从 Mutex* 变为 tr1::shared_ptr<Mutex>。不幸的是,tr1::shared_ptr 的缺省行为是当它所指向的东西的 reference count(引用计数)变为零的时候将它删除,但这不是我们想要的。当我们使用完一个 Mutex 后,我们想要将它解锁,而不是将它删除。

幸运的是,tr1::shared_ptr 允许有一个对 "deleter"(当 reference count(引用计数)变为零时调用的一个 function(函数)或者 function object(函数对象))的特殊说明。(这一功能是 auto_ptr 所没有的,auto_ptr 总是删除它的 pointer(指针)。)deleter 是 tr1::shared_ptr 的 constructor(构造函数)的可选的第二个参数,所以,代码看起来就像这样:

class Lock {
public:
  explicit Lock(Mutex *pm)       // init shared_ptr with the Mutex
  : mutexPtr(pm, unlock)         // to point to and the unlock func
  {                              // as the deleter

    lock(mutexPtr.get());        // see Item 15 for info on "get"
  }
private:
  std::tr1::shared_ptr<Mutex> mutexPtr;    // use shared_ptr
};                                         // instead of raw pointer

在这个例子中,注意 Lock class 是如何不再声明一个 destructor(析构函数)的。那是因为它不需要。Item 5 解释了一个 class 的 destructor(析构函数)(无论它是 compiler-generated(编译器生成)的还是 user-defined(用户定义)的)会自动调用这个 class 的 non-static data members(非静态数据成员)的 destructors(析构函数)。在本例中,就是 mutexPtr。但是,当 mutex(互斥体)的 reference count(引用计数)变为零时,mutexPtr 的 destructor(析构函数)会自动调用 tr1::shared_ptr 的 deleter ——在此就是 unlock。(看过这个 class 的源代码的人多半意识到需要增加一条注释表明你并非忘记了 destruction(析构),而只是依赖 compiler-generated(编译器生成)的缺省行为。)

  • copy the underlying resource(拷贝底层的资源)。有时就像你所希望的你可以拥有一个资源的多个拷贝,你需要一个 resource-managing class(资源管理类)的唯一理由就是确保当你使用完每一个拷贝之后,它都会被释放。在这种情况下,拷贝一个 resource-managing object(资源管理对象)也应该同时拷贝它所包覆的资源。也就是说,拷贝一个 resource-managing object(资源管理对象)需要实行一次 "deep copy"(“深层拷贝”)。

某些标准 string 类型的实现是由 pointers to heap memory(指向堆内存的指针)构成,那里存储着组成那个 string(字符串)的字符。这样的 strings 的 objects 包含一个 pointers to heap memory(指向堆内存的指针)。当一个 string object 被拷贝,一个拷贝应该由那个指针和它所指向的内存两者组成。这样的 strings 表现为 deep copying(深层拷贝)。

  • transfer ownership of the underlying resource(传递底层资源的所有权)。在非常特殊的场合,你可能希望确保只有一个 RAII object 引用一个 raw resource(裸资源),而当这个 RAII object 被拷贝的时候,资源的所有权从 copied object(被拷贝对象)传递到 copying object(拷贝对象)。就像 Item 13 所说明的,这就是被 auto_ptr 使用的 "copy"(“拷贝”)的含意。

copying functions(拷贝函数)(copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符))可能由编译器生成,所以除非 compiler-generated(编译器生成)的版本所做的事正是你想要的(Item 5 说明了这些缺省行为),否则你应该自己编写它们。在某些情况下,你还要支持这些函数的泛型化版本。这样的版本在 Item 45 中描述。

Things to Remember

  • 拷贝一个 RAII object 必须拷贝它所管理的资源,所以资源的拷贝行为决定了 RAII object 的拷贝行为。
  • 通常的 RAII class 的拷贝行为是 disallowing copying(不允许拷贝)和 performing reference counting(实行引用计数),但是其它行为也是有可能的。
基于MATLAB的最大重叠离散小波变换(MODWT)算法详解 最大重叠离散小波变换(Maximal Overlap Discrete Wavelet Transform, MODWT)是DWT的重要改进形式,由Percival和Walden等人在20世纪90年代系统提出并完善。MODWT通过消除下采样操作,采用样本平移不变的分解方式,解决了DWT的多个固有缺陷。MODWT的核心创新在于取消下采样环节。与DWT每级分解后对滤波输出进行二分下采样不同,MODWT在每一级都使用相同的滤波器,但通过插入零值的方式调整滤波器系数,保持每级系数数量与原始信号相同。 阅读详情

相关推荐

【杰理AC696X】ADKEY复用指示灯不定时出现灯不亮或按键失灵的问题

测试SDK版本:《ac696n_soundbox_sdk_v1.5.0-20211217》ADKEY复用指示灯的做法多用于蓝牙小音箱,IC引脚越来越少,复用也就用的越多了,不过,在使用前应评估好功能,因为复用的做法,肯定会牺牲点东西的,如果体验感啥的。 比如这个图,(V-)按键按下时,LED是会一直导通发光的,按不同阻值的按键还有亮度不一致的问题,这样的问题有的客户是不接受的,这就要另寻他法了。某天调好案子,放在一边测试时,发现指示灯不亮了,按键也不起作用,想起之前AC692系列也有类似的问题,很快就定位到

xx842678的博客 1656

14. Thinking carefullly about copying behavior in resource-managing classes

在资源管理类中小心copying行为

xupeng1644的博客 1349

【ESP32 S3 开发】基于I2S协议驱动INMP441数字麦克风 实现语音采集

注意INMP441的 L/R接地之后才稳定输出数据 否则一直都是0WAV格式说明这里主要是想说wav文件重要的就是生成一个wavhead来标识它是一个wave文件,wave文件的data chunk 中的data部分还是PCM编码格式的数据,直接从I2S读进去就可以,不需要压缩。wav文件的头一般由4个chunk组成。 以16bit深度为例,代码如下。其他深度可以根据注释自行计算。 I2S -> DAC i2s_read()函数,向flash中划分的一个buff当中读入byte数组。以16bit深度为例,一

m0_43395703的博客 1万+

C++11 移动语义和完美转发

所谓的移动语义即就是transfer ownership,转移所有权。 而move() 和 farward() 只不过是对类型转换的一个包装而已。

zhouguoqionghai的博客 4354

Delphi中使用FireDAC与uniDAC高效连接PostgreSQL的实战指南

本文为Delphi开发者提供了使用FireDAC与uniDAC组件高效连接PostgreSQL数据库的实战指南。文章详细对比了两款组件的选择策略,重点解析了驱动依赖(libpq.dll)的配置、连接参数设置、常见错误排查及性能优化技巧,旨在帮助开发者快速构建稳定可靠的数据库连接。

tomato的博客 807

恭喜,恭喜,iMouse爱鼠XP版全面发布测试,苹果手机自动化测试必须

电脑控制苹果手机,免越狱,免签名,自动化测试

qq_41057894的博客 1064

C++核心准则边译边学-F.44 在不希望得到拷贝而且不需要返回值为空时返回T&

F.44: Return a T& when copy is undesirable and "returning no object" isn't needed F.44 在不希望得到拷贝而且不需要返回值为空时返回T&。 译者注:不希望的到拷贝的含义是只是调用接口。 Reason(原因) The language guarantees that a T& refe...

面向对象思考 516

Python实战:用NumPy实现LU分解与QR分解(附代码对比)

本文深入探讨了使用NumPy实现LU分解与QR分解的实战方法,通过代码对比分析了两种矩阵分解技术的原理、数值稳定性差异及核心应用场景。LU分解适用于高效求解线性方程组,而QR分解凭借其正交化特性,在求解最小二乘问题和处理病态矩阵时更具优势。文章提供了详细的Python代码示例,帮助开发者根据具体问题选择最合适的分解方法。

php55的博客 1218

C++进阶_Effective_C++第三版(三) 资源管理 Resource Management

上一篇: Effective_C++第三版知识解析梳理(二) 构造/析构/赋值运算 Constructors,Destructors,and Assignment Operators 下一篇: Effective_C++第三版知识解析梳理(四) 设计与声明 Designs and Declarations

hanweilong的博客 718

Python趣学篇:用Pygame打造绚烂流星雨动画

这是一个基于Pygame开发的流星雨动画项目,具有以下特效:🌠多彩流星:8种不同颜色的流星随机生成,每颗都有独特的轨迹✨粒子尾迹:每个流星都带有动态的粒子拖尾效果,增强视觉冲击力🌟闪烁星空:150颗背景星星营造真实的夜空氛围🌌渐变背景:深蓝到黑色的自然渐变,模拟夜空色彩💫斜向轨迹:所有流星都以30-60度角自然斜向下运动🎨中文显示:完美支持中文字体,显示"斜向流星雨来啦!"等文字整个程序运行流畅,达到60FPS的丝滑体验,让你仿佛置身于真实的流星雨之中!

Code_流苏:在代码中寻诗意,在实践中觅真知 6061

【思考模型框架】前景理论(Prospect Theory),认为人们并不是完全理性的决策者,而是根据一些心理上的偏差来评估选项的价值

前景理论,是一种行为经济学理论。前景理论,描述了人们在不确定条件下如何作出决策。前景理论,认为人们并不是完全理性的决策者,而是根据一些心理上的偏差来评估选项的价值。

奶爸的编程之路,也就一周冷个三天 1644

[翻译] Effective C++, 3rd Edition, Item 15: 在 resource-managing classes(资源管理类)中提供对 raw resources(裸资源)的访问

Item 15: 在 resource-managing classes(资源管理类)中提供对 raw resources(裸资源)的访问作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/resource-managing classes(资源管理类)真

艺术编程 4054

MuJoCo Playground 机器人强化学习入门教程(一)

MuJoCo Playground 包含 DM 控制套件、机器人运动和机器人操作环境。env每个环境还与一个环境配置相关联,如果需要,可以覆盖该配置。env_cfg请注意,环境配置包含 sim_dt 和 ctrl_dt。ctrl_dt 决定每个 env.step 的时间间隔。因此,每个 env.step 的模拟步长为 ctrl_dt / sim_dt 倍。其他值得注意的参数还有 vision_config,我们将在基于视觉的笔记本中详细讨论!现在,我们将专注于特权观察。

2506_90492529的博客 4618

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 254

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 394

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 349

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 88

【C++——类和对象(上)】筑基篇:以封装奠定面向对象之基

本文系统梳理C++类与对象的核心知识点,涵盖类的定义、访问限定符(public/private)、类作用域、对象实例化及内存布局。重点解析封装思想、this指针机制、空类大小为1字节的原因,以及类与对象在内存中的存储差异。通过对比C语言结构体与C++类,揭示面向对象设计的优势:数据保护、接口统一、命名隔离与代码可维护性提升。内容由浅入深,适合作为学习笔记与复习参考。

2602_95684935的博客 496

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 307

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 450

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 251

1-10 PostgreSQL中多表连接全解:inner/left/right/full/cross join

本文基于 PostgreSQL 15+ 完整拆解 5 种主流连接:内连接、左连接、右连接、全连接、笛卡尔连接。结合可落地的测试数据,演示每种连接的执行逻辑、返回结果,重点剖析 NULL 值对 JOIN 匹配的核心影响,彻底解决多表连接数据丢失、匹配异常、结果错乱的问题。

zhangjw34的专栏 105

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 802

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 170

c++ explicit关键字详解

explicit。

拒绝AI文,努力在AI时代写出感动人心的好文章 151

825 mW 顶上 212 °C:ASM491S 的 105 °C/W、250 mA 短路电流与热关断

25 °C 环境里结温 34.1 °C,125 °C 环境里结温 134.1 °C——后者已经离 150 °C 只有 16 °C 了,但还在范围内。换个角度看同一件事:从 25 °C 和从 125 °C 起算,温升都是 86.6 °C,但一个到 111.6 °C(还在范围内),一个到 211.6 °C(远远超限)。这个算例里 20 °C/W 是我假设的,实际值取决于机壳材料、密封程度、有没有风扇,跨度可以从 5 °C/W 到 50 °C/W 以上。外面 50 °C 的机箱,盒内就是 63.9 °C。

ANSILIC的博客 393
上一篇: [翻译] Effective C++, 3rd Edition, Item 13: 使用 objects(对象)管理资源
下一篇: [翻译] Effective C++, 3rd Edition, Item 15: 在 resource-managing classes(资源管理类)中提供对 raw resources(裸资源)的访问
fatalerror99
博客等级 码龄24年 166粉丝 20原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值