【C++】《Effective C++》第三章
第三章 资源管理
条款13:以对象管理资源
当申请一块动态内存时,可能会发生内存泄漏。
class Investment {};
void f() {
Investment* pInv = createInvestment();
// ...
delete pInv; // 释放pInv所指对象
}
对于以上程序,发生内存泄漏的情况:
- 忘记
delete
。 - 有
delete
,但是在delete
之前就跳出了控制流。比如提前return
、发生异常等。
- 忘记
解决这种情况比较好的方法是使用对象管理资源,它包括两个关键想法:
- 获得资源后立刻放进管理对象内,即资源取得时机便是初始化时机(
RAII
)。 - 管理对象运用析构函数确保资源被释放。
- 获得资源后立刻放进管理对象内,即资源取得时机便是初始化时机(
#include <memory>
// 使用智能指针auto_ptr,C++11中使用unique_ptr,auto_ptr是其老版本。
void f() {
std::auto_ptr<Investment> pInv(createInvestment());
// std::unique_ptr<Investment> pInv(createInvestment());
// ...
}
// 使用智能指针shared_ptr,推荐使用
void f() {
std::shared_ptr<Investment> pInv = std::make_shared<Investment>(createInvestment());
}
请记住
- 为防止资源泄漏,请使用
RAII
对象,它们在构造函数中获得资源并在析构函数中释放资源。 - 常用的
RAII classes
有unique_ptr
,auto_ptr
,shared_ptr
,推荐使用shared_ptr
。
条款14:在资源管理类中小心copying
行为
并非所有资源都是动态内存,除此之外还有锁等资源,也应该通过"对象管理资源"来确保获取资源后能够正确的释放,根据资源的类型和不同的需求,可能需要定义不同的copy
行为。
- 禁止复制:比如说锁资源,管理锁资源的对象复制通常并不合理。因此应该禁止这类对象的复制。
- 对底层资源使用"引用计数法":如果希望保有资源,直到它的最后一个使用者(某对象)被销毁。这种情况下复制
RAII
对象时,应该将资源的"被引用数"递增。 - 复制底部资源:这种情况下,希望在复制
RAII
对象时,同时复制其关联的底层资源。展现出一种"深拷贝"行为。 - 转移底部资源的拥有:如果希望任一时刻一个资源只由一个
RAII
对象管理,那么在复制RAII
对象时,应该实现拥有权的"转移",原RAII
对象拥有的资源设为null
。
请记住
- 复制
RAII
对象必须一并复制它所管理的资源,所以资源的copying
行为决定RAII
对象的copying
行为。 - 普遍而常见的
RAII class copying
行为是:抑制copying
、施行引用计数法(reference counting
)。不过其他行为也都可能被实现。
条款15:在资源管理类中提供对原始资源的访问
取得RAII
对象所管理资源的办法可以通过显式转换或隐式转换:
- 显式转换(比较安全,但不易用):如
shared_ptr
的get()
方法。 - 隐式转换(比较易用,但不安全):如
shared_ptr
的operator*
和operator->
。
请记住
- APIs往往要求访问原始资源。所以每一个RAII class应该提供一个"取得其所管理之资源"的办法。
- 对原始资源的访问可能经由显式转换和隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便。
条款16:成对使用new
和delete
时要采取相同形式
当使用new
和delete
时,发生两件事:
- new:
- 内存被分配出来(通过
operator new
函数)。 - 针对此内存会有一个(或更多)构造函数被调用。
- 内存被分配出来(通过
- delete:
- 针对此内存会有一个(或更多)析构函数被调用。
- 内存被释放(通过
operator delete
函数)。
std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
// ...
delete stringPtr1;; // 删除一个对象
delete [] stringPtr2; // 删除一个由对象组成的数组
请记住
- 如果你在
new
表达式中使用[]
,必须在相应的delete
表达式中也使用[]
。如果你在new
表达式中不使用[]
,一定不要在相应的delete
表达式中使用[]
。
条款17:以独立语句将newed
对象置入智能指针
// 揭示处理程序的优先权
int priority();
void processWidget(std::shared_ptr<Widget> pw, int priority);
// 调用
processWidget(new Widget, priority());
// 显然上面的调用不对
processWidget(std::shared_ptr<Widget>(new Widget), priority());
虽然如此,上面的调用还是可能泄露资源。因为在调用processWidget
之前,编译器必须创建代码,做以下三件事:调用priority;执行new Widget;调用std::shared_ptr构造函数。但是在C++
中编译器对于这三件事的调用顺序不定,所以当priority
的调用导致异常,可能执行new Widget
返回的指针将会遗失。即在资源被创建和资源被转换为资源管理对象两个时间点之间可能发生异常干扰。
为了解决上面的问题,可以使用分离语句。
// 分成两步调用
std::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());
请记住
- 以独立语句将
newed
对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄露。
【C++】《Effective C++》第三章的更多相关文章
- [Effective Java]第三章 对所有对象都通用的方法
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 对于所有对象都通用方法的解读(Effective Java 第三章)
这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...
- 《Effective Java 第三版》新条目介绍
版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...
- 【软件构造】第三章第三节 抽象数据型(ADT)
第三章第三节 抽象数据型(ADT) 3-1节研究了“数据类型”及其特性 ; 3-2节研究了方法和操作的“规约”及其特性:在本节中,我们将数据和操作复合起来,构成ADT,学习ADT的核心特征,以及如何设 ...
- 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...
- 《Linux内核设计与实现》读书笔记 第三章 进程管理
第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...
- Python黑帽编程3.0 第三章 网络接口层攻击基础知识
3.0 第三章 网络接口层攻击基础知识 首先还是要提醒各位同学,在学习本章之前,请认真的学习TCP/IP体系结构的相关知识,本系列教程在这方面只会浅尝辄止. 本节简单概述下OSI七层模型和TCP/IP ...
- 《Entity Framework 6 Recipes》中文翻译系列 (11) -----第三章 查询之异步查询
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (19) -----第三章 查询之使用位操作和多属性连接(join)
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-16 过滤中使用位操作 问题 你想在查询的过滤条件中使用位操作. 解决方案 假 ...
随机推荐
- linux c++ 内存泄漏检测工具:AddressSanitizer(ASan)
1.介绍 AddressSanitizer(ASan),该工具为gcc自带,4.8以上版本均可以使用. 2.使用 编译的方式很简单,只需要添加 -fsanitize=address -g 即可,如 g ...
- 利用神经网络算法的C#手写数字识别(一)
利用神经网络算法的C#手写数字识别 转发来自云加社区,用于学习机器学习与神经网络 欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 下载Demo - 2.77 MB (原始地址):handwri ...
- 【自用】Notice
读题 不要把 \(\sum a \oplus b\) 看成异或和. 注意读完整,有可能对原有符号有新的约定,不要想当然. 注意模数的 0 数清楚. 卡常&时间 打题之前一般先搞个自己的缺省源. ...
- Python零散知识点记录
1.关于setdefaultencoding之前必须reload(sys): 要在调用setdefaultencoding时必须要先reload一次sys模块,因为这里的import语句其实并不是sy ...
- 移动端 CSS3动画属性
一.transform 转换属性 #1. translate位移 transform : translate(50px,100px); //把元素水平移动 50 像素,垂直移动 100 像素 tran ...
- Spark性能调优篇二之重构RDD架构及RDD持久化
如果一个RDD在两个地方用到,就持久化他.不然第二次用到他时,会再次计算. 直接调用cache()或者presist()方法对指定的RDD进行缓存(持久化)操作,同时在方法中指定缓存的策略. 原文:h ...
- 关于AES-CBC模式字节翻转攻击(python3)
# coding:utf-8 from Crypto.Cipher import AES import base64 def encrypt(iv, plaintext): if len(plaint ...
- Helm 带你飞
文章目录 目录 文章目录 在没使用 Helm之前,向 K8S部署应用,我们要依次部署 deployment. svc 等,步骤较繁琐.况且随着很多项目微服务化,复杂的应用在容器中部署以及管理显得较为复 ...
- 第五章 Gateway--服务网关
欧克 ,我接着上篇第四章 Sentinel–服务容错,继续写下去 开始网关之旅 5.1网关简介 大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务.那么作为客户端要如何去调用 这么多的微服务呢 ...
- SpringBoot从入门到精通教程(七)
今天,我们继续讲SpringBoot整合Redis ,也就缓存,它将与我们的Springboot整合 Redis 简介 Redis 是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语 ...