《Effective C++》 读书笔记之三 资源管理

准备知识:

  1. 所谓资源就是,一旦用了它,将来必须还给系统。最常用的资源是动态分配内存,其他常见的资源有文件描述器、互斥锁、图形界面的字形和笔刷、数据库连接以及网络sockets。

  2. auto_ptr 是个“类指针对象”,就是所谓的智能指针,其析构函数自动对其所指对象调用delete。auto_ptr位于 #include <memory> 头文件。由于auto_ptr被销毁时会自动删除它所指之物,所以一定要注意别让多个auto_ptr指向同一个对象。auto_ptr有个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权。auto_ptr并非管理动态分配资源的神兵利器。

  3. Reference-counting smart pointer(引用计数型智慧指针RCSP)是auto_ptr的一种替代方案。持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除。RCSPs提供的行为类似垃圾回收,不同的是,RCSPs无法打破环状引用(例如两个其实已经没有被使用的对象彼此互指,因而好像还处于“被使用”状态)。

  4. TR1的tr1::shared_ptr 是一个RCSP。

  5. auto_ptr和tr1::shared_ptr两者都在其析构函数内做delete而不是delete[]动作。那意味着动态分配而得的array身上使用auto_ptr或tr1::shared_ptr是不明智的。但是这是可以通过编译的。

//准备知识2 auto_ptr不寻常的性质std::auto_ptr<Investment> pInv(createInvestment());
//pInv指向createInvestment()返回物
std::auto_ptr<Investment> pInv2(pInv);
//现在pInv2指向对象,pInv被置为null
pInv = pInv2;
//现在pInv指向对象,pInv2被置为null

正文


条款13:以对象管理资源 Use objects to manage resource

获取资源后立刻放进管理对象内。(资源取得时机便是初始化时机。Resource Acquisition Is Initialization;简称RAII)

管理对象运用析构函数确保资源被释放。

例子如下:

class Investment{...};
Investment* createInvestment();void f()//auto_ptr版本
{std::auto_ptr<Investment> pInv(createInvestment());// 调用factory函数,使用pInv经由auto_ptr的析构函数自动删除pInv...
}void f()//shared_ptr版本
{...std::tr1::shard_ptr<Investment> pInv(createInvestment());//pInv指向createInvestment()返回物std::tr1::shard_ptr<Investment> pInv2(pInv);//现在pInv,pInv2指向同一对象pInv = pInv2;//无任何改变...
}

重点:

  1. 为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。

  2. 两个常用的RAII classes 分布式tr1::shared_ptr 和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它指向null。

2016-11-03 22:23:43

条款14:在资源管理类中小心copying行为。

当一个RAII对象被复制,有如下几种可能

  1. 禁止复制。

    class Lock:private Uncopyable{ };

  2. 对底层资源祭出“引用计数法”。有时候我们希望保有资源,直到它的最后一个使用者被销毁。这种情况下复制RAII对象时,应该将资源的“被引用数”递增。tr1::shared_ptr便是如此。tr1::shared_ptr允许指定所谓的“删除器”,那是一个函数或函数对象,当引用次数为0时便被调用。删除器对tr1::shared_ptr构造函数而言是可有可无的第二参数。

  3. 复制底部资源。进行深度拷贝。

  4. 转移底部资源的拥有权。采用auto_ptr。

class Lock{
public://以某个Mutex初始化shared_ptr,并以unlock函数作为删除器explicit Lock(Mutex *pm):mutexPtr(pm,unlock){lock(mutexPtr.get());}
private:std::tr1::shared_ptr<Mutex> mutexPtr;
}

重点:

  1. 复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。

  2. 普通而常见的RAII class copying行为是:抑制copying、施行引用计数法。不过其他行为也都可能被实现。

2016-11-03 23:59:30

条款15:在资源管理类中提供对原始资源的访问。

有时候需要一个函数可将 RAII class 对象转换为其所内含之原始资源。有两种做法可以达成目标:

1.显式转换

tr1::shared_ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针。

例子如下:

std::tr1::shared_ptr<Investment> pInv(createInvestment());int daysHeld(const Investment* pi);int days = daysHeld(pInv);//错误!!! 不允许直接使用智能指针,需要获取原始资源。int days = daysHeld(pInv.get());//good!!!将pInv内的原始指针传给daysHeld

2.隐式转换

几乎所有的智能指针都重载了指针取值操作符(operator->和operator*),tr1::shared_ptr和auto_ptr也重载了取值操作符,它们允许隐式转换至底部原始指针。

例子如下:

class Investment{
public:bool isTaxFree() const;...
};Investment* createInvestment();
std::tr1::shared_ptr<Investment> pi1(createInvestment());
bool taxable1 = !(pi1->isTaxFree());//经过operator->访问资源std::tr1::shared_ptr<Investment> pi2(createInvestment());bool taxable2 = !((*pi1).isTaxFree());//经过operator*访问资源

重点:

  1. APIs往往要求访问原始资源,所以每一个RAII class应该提供一个“取得所管理之资源”的办法。

  2. 对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便。

2016-11-04 17:44:52

条款16:成对使用new和delete时要采取相同形式。

当你使用new,有两件事发生。第一,通过operator new的函数内存被分配出来。第二,针对此内存会有一个构造函数被调用。

当你使用delete,有两件事发生。第一,针对此内存会有一个(或多个)析构函数调用。第二,通过operator delete的函数释放内存。

delete的最大问题在于:即将被删除的内存之内究竟存有多少个对象?

当你对一个指针使用delete,唯一能够让delete知道内存中是否存在一个“数组大小记录”的办法就是:由你来告诉它。如果你使用delete时加上中括号[],delete便认定指针指向一个数组,否则它便认定指针指向单一对象。

例子如下:

std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
...
delete stringPtr1;//删除一个对象
delete [] stringPtr2;//删除一个由对象组成的数组

尽量不要对数组形式做typedefs动作。可以使用vector或者string来替代。

重点:

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

2016-11-04 17:54:51

条款17:以独立语句将newed对象置于智能指针。

例子:

int priority();
void processWidget(str::tr1::shared_ptr<Widget> pw,int priority);

调用processWidget:

processWidget(new Widget,priority());//不能通过编译
processWidget(std::tr1::shared_ptr<Widget> (new Widget),priority());//可以通过编译

调用processWidget之前,编译器必须创建代码,做一下3件事:

1.调用priority;

2.执行“new Widget”;

3.调用tr1::shared_ptr构造函数。

由于C++编译器以什么样的次序完成这件事,弹性很大。

一种可能的操作顺序是2,1,3。但是在执行2后,如果执行1时,发生异常,那么2中返回的指针被遗失。

而且3还没有来得及执行,所以2返回的指针没有置入st1::shared_ptr内,所以会发生资源泄漏。

避免这类问题的办法很简单:使用分离语句。分别写出(1)创建Widget;

(2)将它置于一个智能指针内,然后再把那个智能指针传给processWidget:

std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw,priority());

重点:

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

2016-11-04 22:56:02

转载于:https://blog.51cto.com/qiaopeng688/1869161

《Effective C++》 读书笔记之三 资源管理相关推荐

  1. 《需求工程-软件建模与分析之读书笔记之三》

    <需求工程-软件建模与分析之读书笔记之三> 第14章<面向对象建模>采用了面向对象方法学的世界观,将系统看作是一系列对象的集合,每个对象具有独立的职责,完成独立的任务,对象之间 ...

  2. Effective C++读书笔记 摘自 pandawuwyj的专栏

    Effective C++读书笔记(0)       Start   声明式(Declaration):告诉编译器某个东西的名称和类型,但略去细节.   std::size_t numDigits(i ...

  3. more effective c++和effective c++读书笔记

    转载自http://bellgrade.blog.163.com/blog/static/83155959200863113228254/,方便日后自己查阅, More Effective C++读书 ...

  4. 重构(Refactoring)技巧读书笔记 之三

    重构(Refactoring)技巧读书笔记 之三<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:of ...

  5. Effective Java读书笔记(二)

    Effective Java 读书笔记 (二) 创建和销毁对象 遇到多个构造器参数时要考虑使用构建器 创建和销毁对象 何时以及如何创建对象? 何时以及如何避免创建对象? 如何确保它们能够适时地销毁? ...

  6. Effective STL 读书笔记

    Effective STL 读书笔记 标签(空格分隔): 未分类 慎重选择容器类型 标准STL序列容器: vector.string.deque和list(双向列表). 标准STL管理容器: set. ...

  7. Effective Java 读书笔记(七):通用程序设计

    Effective Java 读书笔记七通用程序设计 将局部变量的作用域最小化 for-each 循环优于传统的 for 循环 了解和使用类库 如果需要精确的答案请避免使用 float 和 doubl ...

  8. Effective Java读书笔记完结啦

    Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...

  9. Effective Java 读书笔记(一)

    前言: 开个新的坑位,<effective java>的读书笔记,之后有时间会陆陆续续的更新,读这本书真的感触满多,item01和item02就已经在公司的项目代码中看到过了.今天这篇主要 ...

  10. Effective C++ 读书笔记 Item1-Item4

    目录 守则01:把C++看做一个语言的集合,而不是单一的语言 守则02:尽量使用const, enum, inline, 减少宏变量#define的使用 守则03: 尽可能使用const关键字 守则0 ...

最新文章

  1. Jeff Dean亲自揭秘谷歌下一代AI架构:通用、稀疏且高效
  2. Java主线程等待所有子线程执行完毕再执行解决办法(转)
  3. CentOS下KVM网卡设置成网桥时获取镜像端口的流量
  4. [转]NS2仿真过程中解决动画仿真节点未定义问题
  5. HDU1559 最大子矩阵【DP】
  6. 立创开源 | 基于stm32的稳定输出9v的双向DCDC自动稳压系统
  7. Win10 VS2019+QT/OpenCV/灰点相机/函数信号发生器 配置及其使用
  8. ovm安装过程及中断处理
  9. Java 反射到底慢在哪?
  10. RAC环境诊断案例一则
  11. python描述对象静态特性的数据为_对于需要几个单位共同负担的一张原始凭证上的支出,应根据其他单位负担部分为其提高( )。...
  12. 银行双活数据中心建设项目实践
  13. 几MB的小软件!拯救我们的破手机!
  14. 网络营销与传统营销的区别
  15. AI-多模态-2021:ALBEF
  16. java 迭代器的hasnext,在Python迭代器中具有hasNext?
  17. FTP笔记-FTP主动模式和被动模式
  18. 【Vue3+vite+Element-UI Plus 】
  19. 该地发文,取得一级建造师等注册证书可直接考核认定高级工程师!
  20. 快速了解Java设计模式

热门文章

  1. Tensorflow:常见错误
  2. 怎么用sql按条件把表分离_在做sqlserver数据库sql优化时,这25条事项需要注意
  3. java读取外部配置文件_SpringBoot读取外部配置文件的方法
  4. 力扣-1566 重复至少 K 次且长度为 M 的模式
  5. CSDN 如何删除自己不用的分类(亲测有效!)
  6. 用双网卡实现跨网段访问(转载)
  7. Docker入坑指南之EXEC
  8. BATJ等大厂最全经典面试题分享
  9. difference between match and exec
  10. 记一次噩梦般的经历——论学会反编译的重要性