《Effective C++》第3章 资源管理(1)-读书笔记
章节回顾:
《Effective C++》第1章 让自己习惯C++-读书笔记
《Effective C++》第2章 构造/析构/赋值运算(1)-读书笔记
《Effective C++》第2章 构造/析构/赋值运算(2)-读书笔记
《Effective C++》第3章 资源管理(1)-读书笔记
《Effective C++》第3章 资源管理(2)-读书笔记
《Effective C++》第4章 设计与声明(1)-读书笔记
《Effective C++》第4章 设计与声明(2)-读书笔记
《Effective C++》第8章 定制new和delete-读书笔记
内存只是你必须管理的众多资源之一。其他常见的资源还包括文件描述器(file descriptors)、互斥锁(mutex locks)、图形界面中的字型和笔刷、以及网络sockets。无论哪一种资源,重要的是当你不再使用它时,将它还给系统。
条款13:以对象管理资源
下面有一个Investment继承体系:
class Investment { ... };
Investment是root class,获得Investment体系的对象是通过工厂函数:
Investment* createInvestment(); //返回指针指向Investment继承体系动态分配对象
显然,createInvestment的调用者使用后,有责任删除返回的指针,以释放对象。假设函数f()负责这个行为:
void f()
{
Investment *pInv = createInvestment();
...
delete pInv;
}
但至少存在几种情况使得f()无法删除createInvestment对象:
(1)…区域有一个过早的return语句,导致delete没执行。
(2)…区域抛出异常,控制流不会转向delete。
当delete时,我们泄露的不只是内含Investment对象的那块内存,还包括其所保存的任何资源。
为确保createInvestment()返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开f()时,该对象的析构函数会自动释放那些资源。标准库提供的auto_ptr是个“类指针对象”,即智能指针,其析构函数自动对其所指对象调用delete。
下面是f()的修正版:
void f()
{
std::auto_ptr<Investment> pInv(createInvestment()); //由auto_ptr的析构函数自动删除pInv
...
}
f()说明了以对象管理资源的两个重要方面:
(1)获得资源后立刻放进管理对象内。
以资源管理对象的观念常被称为“资源取得时机便是初始化时机”(Resource Acquisition Is Initialization, RAII)。每一笔资源在获得的同时立刻被放进管理对象中。
(2)管理对象运用析构函数确保资源被释放。
无论控制流如何离开f(),一旦对象离开作用域(对象被销毁),其析构函数被调用,资源被释放。
注意:由于auto_ptr被销毁时会自动删除它所指之物,所以别让多个auto_ptr同时指向某一对象。为了预防这个问题auto_ptr有一个很好的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一权。
std::auto_ptr<Investment> pInv1(createInvestment());
std::auto_ptr<Investment> pInv2(pInv1); //pInv2指向对象,pInv1被设为null
pInv1 = pInv2; //pInv1指向对象,pInv2被设为null
正是因为auto_ptr对象具备“非正常”的拷贝性质,所以不能用于STL容器的元素等。
auto_ptr的替代方案是:引用计数型智能指针(reference-counting smart pointer (RCSP))。RCSP能够追踪共有多少对象指向某种资源,并在无人指向它时自动删除该资源。RCSP提供的行为类似垃圾回收,不同的是它无法打破环状引用,例如,两个其实已经没被使用的对象彼此互指,好像对象还处于“被使用”状态。
TR1的tr1::shared_ptr就是一个RCSP,f()的新写法:
void f()
{
std::tr1::shared_ptr<Investment> pInv(createInvestment());
...
}
f()的新版本与使用auto_ptr几乎相同,但拷贝行为正常了:
void f()
{
...
std::tr1::shared_ptr<Investment> pInv1(createInvestment());
std::tr1::shared_ptr<Investment> pInv2(pInv1); //pInv1和pInv2指向同一对象
pInv1 = pInv2; //pInv1和pInv2指向同一对象
...
}
因为shared_ptr的复制行为是正常的,所以可用于STL容器以及其他auto_ptr非正常复制行为并不适用的情况。
特别注意:auto_ptr和shared_ptr两者都在析构函数内做delete而不是delete[]。所以下面的操作是非常错误的:
std::auto_ptr<std::string> aps(new std::string[]);
std::tr1::shared_ptr<int> spi(new int[]);
之所以没有特别针对C++动态分配数组而设计的类似auto_ptr或tr1::shared_ptr是因为vector和string总是可以取代动态数组,这是你的第一选择。
请记住:
(1)为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
(2)两个常被使用的RAII class分别是:tr1::shared_ptr和auto_ptr。前者是最好的选择,因为其copy行为比较正常。auto_ptr的copy会使被复制物指向null。
条款14:在资源管理类中小心copying行为
并非所有资源都是heap-based的,对这种资源auto_ptr和tr1::shared_ptr这样的智能指针往往不适用。举例如下:
有两个函数用来处理Mutex互斥器对象:
void lock(Mutex *pm); //锁定pm指向的互斥器
void unlock(Mutex *pm); //解锁
为确保不会忘记解锁,可以建立一个class管理锁,这个class基本结构由RAII支配,即“资源在构造期间获得,在析构期间释放”。
class Lock
{
public:
explicit Lock(Mutex *pm) : mutexPtr(pm)
{
lock(mutexPtr);
}
~Lock()
{
unlock(mutexPtr);
}
private:
Mutex *mutexPtr;
};
客户是这样使用的:
Mutex m; //定义所需的互斥器
{
...
Lock ml(&m); //锁定互斥器
...
} //自动解锁
这样使用是可以的。但如果发生copy的情况呢?即当一个RAII对象被复制,会发生什么事情?
Lock ml1(&m);
Lock ml2(ml1);
有四种可能:
(1)禁止复制。
许多时候允许RAII对象被复制并不合理。
(2)对底层资源使用“引用计数法”。
有时候我们希望保存资源,直到它的最后一个使用者被销毁。tr1::shared_ptr便是如此处理。
注意:tr1::shared_ptr的缺省行为是当引用次数为0时删除其所指物,我们想要的是释放锁,而不是删除。我们可以指定“删除器”,这是一个函数,当引用次数为0时会被调用。auto_ptr无此性质,它总是删除它的指针。
更改后的版本是:
class Lock
{
public:
explicit Lock(Mutex *pm) : mutexPtr(pm, unlock) //unlock为删除器
{
lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr; //使用shared_ptr替换raw_pointer
};
注意:这个lock class不需要析构函数了,因为编译器会自动调用mutexPtr的析构函数,当引用计数为0时,自动调用删除器。
(3)复制底部资源
可以针对一份资源拥有任意数量的副本。这是的复制是深拷贝。
(4)转移底部资源的拥有权
你可能希望永远只有一个RAII对象指向一个raw recource,即使RAII对象被复制。这是资源的拥有权会从被复制物转移到目标物。auto_ptr便是如此。
注意:copying函数(包括copy构造函数和copy assignment操作符)有可能被编译器创造出来,因此除非编译器版本做了你想做的事,否则你要自己编写它们。
请记住:
(1)复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。
(2)普遍常见的RAII class copying行为是:禁止copying、使用引用计数。其他行为也可能被实现。
《Effective C++》第3章 资源管理(1)-读书笔记的更多相关文章
- 《TCP/IP详解卷1:协议》第2章 链路层-读书笔记
章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...
- 高性能MySQL(第4版) 第一章 MySQL架构 读书笔记
这本书去年11月出的,今年中文版也出了,并且直接上了微信读书,之后有空就读一读,分享下读书笔记~ 原文内容比较充实,建议有时间可以读一下原文. 第一章主要是个概览. MySQL的逻辑架构 默认情况下, ...
- 《C++primer》v5 第8章 IO库 读书笔记 习题答案
8.1.8.2 这一章不咋会啊.. istream &read(istream &is) { int a; auto old_state=is.rdstate(); is.clear( ...
- Java核心技术卷一基础知识-第12章-泛型程序设计-读书笔记
第12章 泛型程序设计 本章内容: * 为什么要使用泛型程序设计 * 定义简单泛型类 * 泛型方法 * 类型变量的限定 * 泛型代码和虚拟机 * 约束与局限性 * 泛型类型的继承规则 * 通配符类型 ...
- Java核心技术卷一基础知识-第7章-图形程序设计-读书笔记
第7章 图形程序设计 本章内容: * Swing概述 * 创建框架 * 框架定位 * 在组件中显示信息 * 处理2D图形 * 使用颜色 * 文本使用特殊字体 * 显示图像 本章主要讲述如何编写定义屏幕 ...
- Java多线程编程核心技术-第7章-拾遗增补-读书笔记
第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...
- < 利用Python进行数据分析 - 第2版 > 第五章 pandas入门 读书笔记
<利用Python进行数据分析·第2版>第五章 pandas入门--基础对象.操作.规则 python引用.浅拷贝.深拷贝 / 视图.副本 视图=引用 副本=浅拷贝/深拷贝 浅拷贝/深拷贝 ...
- Javascript模式(第四章函数)------读书笔记
一 背景 js函数的两个特点:1 函数是第一类对象(first-class object):2 函数可以提供作用域 1 函数是对象: 1 函数可以在运行时动态创建,还可以在程序执行过程中创建 2 可以 ...
- Javascript模式(第一章简介)------读书笔记
一:模式 模式是一个通用问题的解决方案,可以提供一个更好的实践经验.有用的抽象化表示和解决一类问题的模板. 本书主要讨论如下三种类型的模式 1 设计模式:可复用面向对象软件的基础,包括singleto ...
- 【我所理解的Cocos2d-x】第六章 精灵Sprite 读书笔记
简介: 精灵是2D游戏里最重要的元素.游戏场景中大部分可见的元素都直接或间接地与精灵相关. 在Cococs2d-xz中,精灵使用Sprite表示,它将一张纹理的一部分或者全部的矩形区域绘制在屏幕上. ...
随机推荐
- 对于近阶段公司代码 review 小结
来新公司,给公司的SDK review了一下.发现了不少小问题,在此总结一下. (我下面说明问题可能是很简单,但是搞清楚某些问题还是花了些时间的,大家引以为戒吧) 先谈谈处理的问题: 1.某天QA说有 ...
- Delphi7编译时,发生Access violation at address 00A7B628 in module 'dcc70.dll'. Read of address 00000000.(Delphi6升级到Delphi7)
最近接了一个项目,要求使用Delphi7来开发程序,可是由于我们之前均使用delphi6来开发程序的,而且Delphi6使用很长时间了,积累并改造了第三方控件很成熟了: 故把Delphi6的控件移植( ...
- Cisco 交换Vlan配置
添加Vlan命令 #添加vlan100 config)#vlan 100 #重命名vlan100 config-vlan)#name vlan100 #返回上一层 config-vlan)#exit ...
- 20145302张薇《Java程序设计》第八周学习总结
20145302 <Java程序设计>第八周学习总结 教材学习内容总结 第十五章 日志API简介 使用日志的起点是Logger类,要取得Logger类,必须使用Logger的静态方法get ...
- ThinkPHP将上传问件添加到数据库
<?php namespace Home\Controller; /***************** use Think\Controller; ****命名空间****/ class Mes ...
- SEM关键词的三种分类方式
关键词分类是为了使sem账户搭建结构清晰便于管理关键词.基于对需求人群的深入分析,每个账户都有其独特的分类方式,比如招商加盟行业更多的是地域分类,品牌类企业通常用词性分类即可,而冷门行业用人群分类比较 ...
- Putting Apache Kafka To Use: A Practical Guide to Building a Stream Data Platform-part 1
转自: http://www.confluent.io/blog/stream-data-platform-1/ These days you hear a lot about "strea ...
- 机器学习 delay learning
计蒜之道总决赛考了机器学习,大多数人都不会所以现场学,然后我看了一些之后放弃了..采取了人力分析的办法,最后果然被学习能力碾压.. 不过机器学习看起来是很有趣的,也听别人说了很多,和别人聊了一些,如果 ...
- python 贪婪和非贪婪模式
这样的正则表达式: r'\*(.+)\*' 如果想要匹配*something*这样的一个串按道理说是没问题的 但是如果文本是*this* is *something* 那么我们的正则表达式就会采取贪 ...
- 用gitolite搭建git server
在Ubuntu上测试安装一下git server,为后面项目的代码管理做准备.记录流水账如下, 中间关于git 命令的使用说明不做过多解释,需要了解的请google或者直接git help: 我用到了 ...