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

三. 资源管理

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.mzph.cn/pingmian/30968.shtml

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

相关文章

2024年6月20日 (周四) 叶子游戏新闻

超市播音系统: 定时播放不同音乐 强制卸载软件: 一款强制卸载软件 免费多人沙盒游戏《宝藏世界》推出更新“潮起潮落”&#xff0c;带来全新克苏鲁风冒险准备好迎接一场超凡的冒险吧&#xff0c;MMORPG发行商gamigo宣布《宝藏世界》的最新更新&#xff1a;“潮起潮落”。这次更…

探索Linux命令的新利器:linux-command

在Linux操作系统中&#xff0c;熟练掌握各种命令是成为一名高效开发者或管理员的关键。然而&#xff0c;即使是经验丰富的用户&#xff0c;有时也会遇到命令用法不熟悉或者记忆模糊的情况。这时&#xff0c;一个功能强大的命令搜索工具就显得格外重要。最近在逛github的时候正好…

代码随想录算法训练营第29天(贪心)|455.分发饼干、376. 摆动序列、53. 最大子序和

455.分发饼干 题目链接&#xff1a;455.分发饼干 文档讲解&#xff1a;代码随想录 状态&#xff1a;so easy 思路&#xff1a;对胃口和饼干大小排序&#xff0c;小胃口对应小饼干&#xff0c;不满足的话用下一块饼干试探。 题解&#xff1a; public int findContentChildren(i…

CSS--解决图片变形的方法

原文网址&#xff1a;CSS--解决图片变形的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍html文件中图片变形的解决方法。 问题描述 我们经常需要指定所有图片的大小&#xff0c;让它们排列起来时看起来更整齐。但是&#xff0c;如果我们指定了width和height&#xff0c;那…

力扣41 缺失的正数

给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 [1,2] 中的数字都在数组中…

volatile关键字(juc编程)

volatile关键字 3.1 看程序说结果 分析如下程序&#xff0c;说出在控制台的输出结果。 Thread的子类 public class VolatileThread extends Thread {// 定义成员变量private boolean flag false ;public boolean isFlag() { return flag;}Overridepublic void run() {// 线…

灵感互娱U3D笔试题

文章目录 题目1解析 题目2解析 题目3解析 题目4数组链表 题目5解析 题目6解析 题目7解析题目8解析 后话 题目1 以下C#代码的输出顺序是什么 namespace ConsoleApp2 {internal class Program{class A{ public A(string text){Console.WriteLine(text);}}class B{static A a1 …

原子性(juc编程)

原子性 概述&#xff1a;所谓的原子性是指在一次操作或者多次操作中&#xff0c;要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断&#xff0c;要么所有的操作都不执行&#xff0c;多个操作是一个不可以分割的整体。 //比如说&#xff1a;你喂你女朋友吃冰淇…

ScheduledExecutorService引起的线上问题(抛出异常后不继续执行)

线上有一个服务&#xff0c;采用ScheduledExecutorService定时任务刷新数据库数据到本地缓存作为路由信息 private ScheduledExecutorService scheduledExecutorService Executors.newScheduledThreadPool(1);scheduledExecutorService.scheduleWithFixedDelay(new Runnable()…

【docker】adoptopenjdk/openjdk8-openj9:alpine-slim了解

adoptopenjdk/openjdk8-openj9:alpine-slim 是一个 Docker 镜像的标签&#xff0c;它指的是一个特定的软件包&#xff0c;用于在容器化环境中运行 Java 应用程序。 镜像相关的网站和资源&#xff1a; AdoptOpenJDK 官方网站 - AdoptOpenJDK 这是 AdoptOpenJDK 项目的官方网站&…

Nginx Rewrite技术

一&#xff1a;理解地址重写 与 地址转发的含义。二&#xff1a;理解 Rewrite指令 使用三&#xff1a;理解if指令四&#xff1a;理解防盗链及nginx配置 简介&#xff1a;Rewrite是Nginx服务器提供的一个重要的功能&#xff0c;它可以实现URL重定向功能。 一&#xff1a;理解地…

数据库异常恢复2-备份文件恢复(快速恢复的手动启动方式)

(四) 备份文件备份恢复的概念 本次所说的数据恢复有异于数据的导入导出 1. 备份工具 gbase8s数据库提供了两种工具进行完成系统物理备份、逻辑日志备份和系统恢复&#xff1a;ontape和onbar ontape&#xff1a;提供了基本的系统物理备份、日志备份和恢复能力&#xff0c;其…

C++基础知识——命名空间

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;Yan. yan. 文章目录 1、什么是命名空间2、命名空间的作用3、如何定义命名…

js实现canvas截图功能

关键代码 使用canvas的导出功能和drawImage函数 class CropShape{cropShape(shape){let {x,y,w,h} shapeconsole.log(x,y,w,h)const roiCanvas document.createElement(canvas);document.getElementById(app).append(roiCanvas)const roiCtx roiCanvas.getContext(2d);roi…

贝叶斯优化、高斯过程相关概念总结

目录 贝叶斯优化 高斯过程 采集函数 贝叶斯优化 贝叶斯优化 &#xff5c; 黑盒优化全局最优方法 &#xff5c; Bayesian Optimization_哔哩哔哩_bilibili 贝叶斯优化用于解决寻找某个函数的最大值/最小值&#xff0c;在自变量维度比较小时(<20)表现的非常好。 适用…

Android5.1 文件AES加密

密钥最低128bits public class FileEncryptionUtil {private static final String ALGORITHM "AES";private static final String TRANSFORMATION "AES";/*** 使用AES加密算法对文件进行加密。** param key 用于加密的密钥。.* param inputFiles …

《计算机英语》Unit1 计算机概述

期末试卷组成 1、选择20道 2、判断20道 3、词汇翻译&#xff08;单词词组&#xff0c;参照课后习题&#xff09; 4、翻译2道&#xff08;一道原题&#xff0c;参照作业&#xff09; Unit One Computer Overview 单元1 计算机概述 algorithm n. 算法 operate …

byte[]转MultipartFile、byte[]转File一次看个够

目录 需求背景 当你需要将byte[]、MultipartFile、File实现互转时&#xff0c;无外乎以下场景&#xff1a; 保存第三方接口返回二进制流前/后端文件流上传微服务间调用文件格式转换 正如你所需要的&#xff0c;通过搜索引擎筛选到我的本篇文章是因为你在开发中需要将byte[]转…

k8s学习笔记(一)

configMap 一般用来存储配置信息 创建configMap 从文件中获取信息创建&#xff1a;kubectl create configmap my-config --from-file/tmp/k8s/user.txt 直接指定信息&#xff1a; kubectl create configmap my-config01 --from-literalkey1config1 --from-literalkey2confi…

深度学习之计算机视觉

神经网络简介 全连接层和卷积层的根本区别在于权重在中间层中彼此连接的方式。图5.1描述了全连接层或线性层是如何工作的。 在计算机视觉中使用线性层或全连接层的最大挑战之一是它们丢失了所有空间信息&#xff0c;并且就全连接层使用的权重数量而言复杂度太高。例如&#xf…