Effective C++ 改善程序与设计的55个具体做法笔记与心得 3

news/2024/10/18 22:34:28/

三. 资源管理

13. 以对象管理资源

请记住

  • 为防止资源泄露,使用智能指针

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

请记住

  • 复制RAII对象必须一并复制他所管理的资源,所以资源的copying行为决定RAII对象的copying行为
  • 普遍而常见的RAII class copying行为是:抑制copying、施行引用计数法(reference counting)。不过其他行为也都可能被实现。

解释
‌‌‌‌  RAII(资源获取即初始化)是C++中一个非常重要的概念,它用于管理对象生命周期内的资源分配和释放问题。

‌‌‌‌  对于复制RAII对象,首要问题就是如何处理它所管理的资源。实际上,资源的复制行为决定了RAII对象的复制行为,不同的策略会对管理资源的对象的复制产生不同的影响:

  • 抑制复制(Prohibit Copying):既简单又有效。在这种情况下,复制构造函数和赋值运算符通常被声明为private,并且不传递任何实现。这阻止了复制和赋值。这是一个好的默认行为,通常适用于管理不可共享资源的类(如文件、线程、互斥锁等)。
class Uncopyable {
private:Uncopyable(const Uncopyable&);Uncopyable& operator=(const Uncopyable&);
};
  • 引用计数(Reference Counting):这种方法允许复制,同时确保原始资源的生命周期恰到好处。具体来说,只有当最后一个引用被销毁时,才会清理底层资源。std::shared_ptr是一种典型的实现引用计数的RAII类。
std::shared_ptr<int> p(new int[10]);
std::shared_ptr<int> q = p;  // 引用计数增加
  • 深度复制(Deep Copying):在这种策略中,我们为每个RAII对象创建一份新的资源副本。这比引用计数更安全,但可能更为费时,因为它需要复制所有数据。

  • 转移资源所有权(Transfer Ownership):通过移动语义(C++11开始提供),可以将资源从一个对象转移到另一个对象,而不需要复制。这通常适用于管理大块资源的RAII对象,例如std::unique_ptr

根据特定的类管理的资源类型和预期的用途,可以选择适合的策略来处理RAII对象的复制行为。这是面向对象的设计的一个重要方面。

15. 在资源管理类中提供对原始资源的访问

请记住

  • APIs往往要求访问原始资源,所以每一个RAI class应该提供一个“取得其所管理之资源”的办法。
  • 对原始资源的访问可能经由显式转换或隐式转换。一般而言显示转换比较安全,但隐式转换对客户比较方便。

解释
‌‌‌‌  资源获取即初始化(RAII)的类确实经常需要提供获取其管理的资源的方式。这样的设计并不会违背RAII原则,因为资源的所有权仍然在RAII对象中,但一些情况下确实需要让程序员能够访问这些底层资源。

这些底层资源的访问方式主要分为两种:

  • 显式转换:这样的设计使得访问者必须显示地请求访问资源。这样可以减少因误用底层资源而导致的问题。std::unique_ptr就是一个很好的例子,它提供了.get()方法来显式获取底层的原始指针。
std::unique_ptr<int> p(new int());
int* raw = p.get();
  • 隐式转换:这提供了对使用者更为简单的访问方式,但是如果使用不当,可能带来风险。比如,std::shared_ptr可以被隐式转化为bool类型,这使得我们能够在条件语句中使用。
std::shared_ptr<int> p(new int());
if (p) {// 如果 p 管理着一个对象,那么将会执行这里的代码
}

‌‌‌‌  一般来说,显式转换更为安全,因为它强制要求使用者意识到他们正在直接操作底层资源。然而,隐式转换可以使代码更简洁,提供更好的用户体验。因此,应根据特定的使用场景和对安全性的要求来确定哪种方法更为合适。

16. 成对使用new和delete时要采取相同形式

请记住

  • 如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。
  • 如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。

解释
‌‌‌‌  在C++中,这个规则非常重要,因为不正确地使用new[]delete[]会导致未定义的行为。

  1. 当我们使用new[]来分配数组时,必须使用delete[]来删除该数组。如果我们只用delete,就可能会出现内存泄漏。
int* array = new int[10];
delete[] array; // 正确的使用方式
  1. 另一方面,当我们使用new分配单个元素时,必须使用delete(不带[])来删除。如果我们使用delete[]来删除非数组类型的指针,那也会导致未定义的行为。
int* single = new int();
delete single; // 正确的使用方式

‌‌‌‌  这组规则是相当重要的,因为它们涉及到C++运行时环境如何在堆上分配和管理内存。遵循这些规则不仅可以避免内存泄漏,也能防止程序因为错误的内存释放操作而崩溃。

newnew[]以及deletedelete[]的区别主要在于它们处理的数据结构和内存分配的方式:

  1. newdelete:这两个操作符主要用于分配和释放单个对象的内存。
int* p = new int;       // 分配一个整数的内存
delete p;               // 释放该整数的内存
  1. new[]delete[]:这两个操作符用于分配和释放数组的内存。
int* arr = new int[10]; // 分配10个整数的内存
delete[] arr;           // 释放这10个整数的内存

‌‌‌‌  对于这两对操作符,最重要的一点是必须配对使用。也就是说,我们不能使用new来分配内存然后使用delete[]释放内存,反之亦然。

‌‌‌‌  注意:newnew[]分配的内存不会被自动释放,我们需要手动调用相应的delete或者delete[]来释放这些内存,否则将导致内存泄漏。这是一个常见的编码错误,也是使用RAII(资源获取即初始化)技术的主要原因,它可以通过在对象的生命周期结束时自动释放其拥有的资源,从而防止内存泄漏。

17. 以独立语句将newed对象置入智能指针

请记住
‌‌‌‌  以独立语句将newed对象存储于智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄露。

解释
‌‌‌‌  智能指针主要的优点就是它可以保证在任何情况下,包括出现异常时,都可以正确地释放内存。在C++中,如果我们创建了一个普通的指针并使用new分配了内存,但在释放这个内存前出现了异常,那么这个内存就会泄露。

‌‌‌‌  由于异常可能在new之后的任何时间发生,所以我们无法确定何时捕获并处理这个异常。因此,为了避免内存泄露,我们应该尽快将这个指针转换为智能指针。如果我们在创建智能指针对象的同一条语句中使用new,那么即使出现异常,内存也会安全地释放。

std::unique_ptr<int> ptr(new int(10));    // 创建一个存储int的智能指针

‌‌‌‌  虽然将new和智能指针的初始化放在同一行更简洁,但为了避免new在智能指针接管之前就抛出异常导致的任何可能的资源泄露,提倡以独立语句将newed对象存储于智能指针内。如:

int* temp = new int(10);
std::unique_ptr<int> ptr(temp);    // 创建一个存储int的智能指针

‌‌‌‌  这样,即使在new和智能指针的初始化之间抛出异常,我们也可以保证delete temp;被调用,防止了内存泄漏。


http://www.ppmy.cn/news/1470442.html

相关文章

EXCEL数据导入HIVE

引言 本文将论述如何将Windows本地的excel表数据&#xff0c;导入到虚拟机Linux系统中的Hadoop生态中的Hive数据仓库中。 实验准备 DBeaver Hive3.1&#xff08;Hadoop3.1&#xff09; excel数据表 实验步骤 一、首先打开虚拟机&#xff0c;启动Hadoop&#xff0c;启动h…

视觉SLAM14精讲——相机与图像3.3

视觉SLAM14精讲 三维空间刚体运动1.0三维空间刚体运动1.1三维空间刚体运动1.2李群与李代数2.1相机与图像3.1相机与图像3.2 视觉SLAM14精讲——相机与图像3.3 视觉SLAM14精讲相机投影流程双目相机模型 相机投影流程 至此&#xff0c;有关相机三维刚体变换的所有因素已经汇集。…

英语恶补ing

ing的词组都有停下来做某事的感觉了。 second hand是形容词了。 wouldnt buy这里的would是情态动词&#xff0c;也是助动词 助动词不能单独使用&#xff0c;要搭配实义动词&#xff0c;这样才能构成谓语 情态动词&#xff08;modals&#xff09;在英语中有多种作用&#xff…

小摩法兴纷纷转多,看涨港股的时机来了吗?

恒生指数今日高开一度上涨89点报18520点&#xff0c;创近两周高。之后持续震荡下行&#xff1b;恒指临近中 午跌幅扩大&#xff0c;恒生科技指数一度跌近1.5%。截止收盘&#xff0c;恒生指数跌0.52%&#xff0c;盘面上&#xff0c;石油、煤炭、环保、建筑节能等板块涨幅居前&a…

在Elasticsearch中-SpaceJam一个全文搜索的实例

在Elasticsearch中进行全文搜索通常涉及几个步骤&#xff1a;创建索引、定义映射、索引文档、执行搜索查询。下面我将通过一个名为"SpaceJam"的虚构实例来演示如何进行全文搜索。 ### 步骤 1: 创建索引 首先&#xff0c;我们需要创建一个索引。在这个例子中&#x…

计算机网络:网络层 - 虚拟专用网 VPN 网络地址转换 NAT

计算机网络&#xff1a;网络层 - 虚拟专用网 VPN & 网络地址转换 NAT 专用地址与全球地址虚拟专用网 VPN隧道技术 网络地址转换 NAT网络地址与端口号转换 NAPT 专用地址与全球地址 考虑到 IP 地址的紧缺&#xff0c;以及某些主机只需要和本机构内部的其他主机进行通信&…

【AIOps】基于AIOps的故障根因分析及定位

基础知识&#xff1a; 基于机器学习的异常检测与分析技术 传统做法&#xff1a; 复杂运维场景下&#xff0c;如何实现分钟级的故障根因定位 结合大模型&#xff1a; 基于大语言模型的云故障根因分析&#xff5c;顶会EuroSys24论文

【Python日志模块全面指南】:记录每一行代码的呼吸,掌握应用程序的脉搏

文章目录 &#x1f680;一、了解日志&#x1f308;二、日志作用&#x1f308;三、了解日志模块⭐四、日志级别&#x1f4a5;五、记录日志-基础❤️六、记录日志-处理器handler&#x1f3ac;七、记录日志-格式化记录☔八、记录日志-配置logger&#x1f44a;九、流程梳理 &#x…