第三章 资源管理

条款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 classesunique_ptrauto_ptrshared_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_ptrget()方法。
  • 隐式转换(比较易用,但不安全):如shared_ptroperator*operator->

请记住

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

条款16:成对使用newdelete时要采取相同形式

当使用newdelete时,发生两件事:

  • 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++》第三章的更多相关文章

  1. [Effective Java]第三章 对所有对象都通用的方法

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  2. 对于所有对象都通用方法的解读(Effective Java 第三章)

    这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...

  3. 《Effective Java 第三版》新条目介绍

    版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...

  4. 【软件构造】第三章第三节 抽象数据型(ADT)

    第三章第三节 抽象数据型(ADT) 3-1节研究了“数据类型”及其特性 ; 3-2节研究了方法和操作的“规约”及其特性:在本节中,我们将数据和操作复合起来,构成ADT,学习ADT的核心特征,以及如何设 ...

  5. 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...

  6. 《Linux内核设计与实现》读书笔记 第三章 进程管理

    第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...

  7. Python黑帽编程3.0 第三章 网络接口层攻击基础知识

    3.0 第三章 网络接口层攻击基础知识 首先还是要提醒各位同学,在学习本章之前,请认真的学习TCP/IP体系结构的相关知识,本系列教程在这方面只会浅尝辄止. 本节简单概述下OSI七层模型和TCP/IP ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (11) -----第三章 查询之异步查询

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (19) -----第三章 查询之使用位操作和多属性连接(join)

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-16  过滤中使用位操作 问题 你想在查询的过滤条件中使用位操作. 解决方案 假 ...

随机推荐

  1. vertical-align什么时候使用?常用的值分别有什么作用?

    设置元素的垂直对齐方式 常用的值: 1.baseline:默认.元素放置在父元素的基线上. 2.sub:垂直对齐文本的下标. 3.super:垂直对齐文本的上标 4.top:把元素的顶端与行中最高元素 ...

  2. 条件循环语句组成了Python代码的骨架

    条件控制 我们都知道流程图是有多个分支的,程序中也是如此,在Python中是用if语句来判断程序该走哪个分支的.它的执行过程如下: 代码执行过程如下: if if语句的一般形式如下: if condi ...

  3. 深度剖析目标检测算法YOLOV4

    深度剖析目标检测算法YOLOV4 目录 简述 yolo 的发展历程 介绍 yolov3 算法原理 介绍 yolov4 算法原理(相比于 yolov3,有哪些改进点) YOLOV4 源代码日志解读 yo ...

  4. 助力用户选择更优模型和架构,推动 AI机器视觉落地智能制造

      智能制造的全新 "视界" 由互联网大潮掀起的技术进步,推动着智能制造成为传统制造行业面向未来.寻求突破的关键路径.通过融合机器人.大数据.云计算.物联网以及 AI 等多种技术, ...

  5. elastic-job分布式调度与zookeeper的简单应用

    一.对分布式调度的理解 调度->定时任务,分布式调度->在分布式集群环境下定时任务这件事 Elastic-job(当当⽹开源的分布式调度框架) 1 定时任务的场景 定时任务形式:每隔⼀定时 ...

  6. 一、什么是Jmeter?Jmeter安装?Jmeter的启动?

    什么是Jmeter Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具,也可以进行接口测试.它是一个开源的,100%基于Java的应用程序,带有图形界面.它旨在分析 ...

  7. Jmeter之登录接口参数化实战

    为了纪念我走过的坑(为什么有些简单的问题就是绊住我了,还是不够细啊) Jmeter之接口登录参数化实战 因为想要在登录时使用不同的数据进行测试,所以我选择了将数据进行参数化.因为涉及到新建一个接口的功 ...

  8. 2020-2021-1 20209307《Linux内核原理与分析》第七周作业

    这个作业属于哪个课程 <2020-2021-1Linux内核原理与分析)> 这个作业要求在哪里 <2020-2021-1Linux内核原理与分析第七周作业> 这个作业的目标 & ...

  9. IO流(03)--序列化流、打印流

    序列化流 Java提供了一种对象序列化的机制,用一个字节序列可以表示一个对象,该字节序列包含该对象的数据.对象的类型和对象中存储的属性等信息.字节序列写入到文件中后,就相当于在文件中保存了一个对象信息 ...

  10. day112:MoFang:种植园使用websocket代替http&服务端基于flask-socketio提供服务&服务端响应信息&种植园页面显示初始化

    目录 1.种植园使用websocket代替http 2.服务端基于socket提供服务 3.服务端响应信息 4.种植园页面展示 1.种植园使用websocket代替http 我们需要完成的种植园,是一 ...