章节回顾:

《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++》第5章 实现-读书笔记

《Effective C++》第8章 定制new和delete-读书笔记


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

许多API直接指涉资源,所以除非你永远不用它们,否则都会绕过资源管理对象直接访问原始资源。假设使用tr1::shared_ptr管理对象。

std::tr1::shared_ptr<Investment> pInv(createInvestment());

函数daysHeld的声明是这样的:

int daysHeld(const Investment *pi);

下面这种调用方式,肯定是错误的:

int days = daysHeld(pInv);        //错误

因为函数需要的是指针,你传递是一个tr1::shared_ptr<Investment>对象。所以你需要一个函数将RAII对象转换为所内含的原始资源。有两种方法:隐式转换和显示转换。

(1)显示转换

tr1::shared_ptr和auto_ptr都提供了一个成员函数get返回内部的原始指针,这是显式转换。

int days = daysHeld(pInv.get());    //好的,没有问题

(2)隐式转换

tr1::shared_ptr和auto_ptr都重载了操作符operator->和operator*,这样就允许隐式转换到原始指针。举例:假设Investment类有个成员函数bool isTaxFree() const;那么下面的调用是OK的:

bool taxable1 = !(pInv->isTaxFree());        //好的,没有问题
bool taxable2 = !((*pInv).isTaxFree()); //好的,没有问题

现在的问题是,需要原始指针的地方(例如,函数形参),如何以智能指针代替。解决方法是:提供一个隐式转换函数。下面举个字体类的例子:

FontHandle getFont();                    //取得字体句柄
void releaseFont(FontHandle fh); //释放句柄
class Font
{
public:
explicit Font(FontHandle fh) : f(fh){}
~Font()
{
releaseFont(f);
}
private:
FontHandle f;
};

如果C API处理的是FontHandle而不是Font对象,当然你可以像tr1::shared_ptr和auto_ptr那样提供一个get()函数:

FontHandle get() const { return f; }    //显示转换函数

这样是可以的,但客户还是觉得麻烦,这时候定义一个隐式转换函数是必须的。

class Font
{
public:
...
operator FontHandle() const { return f; }
...
};

注意:假设你已经知道了隐式转换函数的用法。例如:必须定义为成员函数,不允许转换为数组和函数类型等。

完成了以上工作,对于下面这个函数的调用是OK的:

void changeFontSize(FontHandle f, int newSize);
Font f(getFont());
int newFontSize;
changeFontSize(f, newFontSize); //好的,Font隐式转换为FontHandle了。

隐式类型转换也增加了一种风险。例如有以下代码:

Font f1(getFont());
FontHandle f2 = f1; //将Font错写成FontHandle了,编译仍然通过。

f1被隐式转换为FontHandle,这时f1和f2共同管理某个资源,f1被销毁,字体释放,这时候你可以想象f2的状态(原谅我这个词我不会说),再销毁f2,必然会造成运行错误。通常提供一个显示转换get函数是比较好的,因为它可以避免非故意的类型转换的错误,这种错误估计会耗费你很长的调试时间(我遇到过的情况)。

请记住:

(1)有些API要求访问原始资源,所以每一个RAII class应该提供一个“取得其所管理的资源”的办法。

(2)对原始资源的访问可以通过显式转换或隐式转换。一般显式转换比较安全,但隐式转换对客户比较方便。


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

我相信你肯定一眼看出以下代码的问题:

std::string *stringArray = new std::string[];
delete stringArray;

程序行为是未定义的。stringArray所含的100个string对象,99个可能没被删除,因为它们的析构函数没被调用。

delete必须要知道的是:删除的内存有多少个对象,决定了调用多少个析构函数。

单一对象和数组的内存布局肯定是不同的,数组占用的内存也许包含一个“数组大小”的记录。(编译器干的事)你可以告诉编译器删除的是数组还是单一对象:

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

这个规则很简单,但有一点需要注意:

typedef std::string AddressLines[];
std::string *pal = new AddressLines; delete pal; //不好,行为未定义。
delete [] pal; //很好。

所以,最好不要对数组做typedef。

请记住:如果new表达式中使用了[],必须在相应的delete表达式中使用[];如果new表达式中不使用[],一定不要在相应的delete表达式中使用[]。


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

假设有以下函数,具体含义我们可以先忽略:

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

先说明一点的是,当你以下面形式调用processWidget时,肯定是错误的:

processWidget(new Widget, priority());

编译出错。tr1::shared_ptr构造函数需要一个原始指针,但该构造函数是explicit,即禁止隐式类型转换的。所以,正常情况你应该这样调用:

processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());    //好的,没有问题

以这种调用方式,执行函数体之前,有三个工作要做:

(1)调用priority。

(2)执行"new Widget"。

(3)调用tr1::shared_ptr构造函数

可以肯定的是(2)在(3)前面执行,但(1)的执行次序不能确定。假设(1)在第二个被执行,则如果调用priority出现异常,new Widget返回的指针将会遗失,因为还未执行tr1::shared_ptr的构造函数。所以,发生了资源泄露。

避免这类问题很简单:使用分离语句。

std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority()); //绝不会造成泄露

请记住:以独立语句将newd对象存储于智能指针内。如果不这样做,一旦异常被抛出,有可能造成难以察觉的资源泄露。

《Effective C++》第3章 资源管理(2)-读书笔记的更多相关文章

  1. 《TCP/IP详解卷1:协议》第2章 链路层-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  2. 高性能MySQL(第4版) 第一章 MySQL架构 读书笔记

    这本书去年11月出的,今年中文版也出了,并且直接上了微信读书,之后有空就读一读,分享下读书笔记~ 原文内容比较充实,建议有时间可以读一下原文. 第一章主要是个概览. MySQL的逻辑架构 默认情况下, ...

  3. 《C++primer》v5 第8章 IO库 读书笔记 习题答案

    8.1.8.2 这一章不咋会啊.. istream &read(istream &is) { int a; auto old_state=is.rdstate(); is.clear( ...

  4. Java核心技术卷一基础知识-第12章-泛型程序设计-读书笔记

    第12章 泛型程序设计 本章内容: * 为什么要使用泛型程序设计 * 定义简单泛型类 * 泛型方法 * 类型变量的限定 * 泛型代码和虚拟机 * 约束与局限性 * 泛型类型的继承规则 * 通配符类型 ...

  5. Java核心技术卷一基础知识-第7章-图形程序设计-读书笔记

    第7章 图形程序设计 本章内容: * Swing概述 * 创建框架 * 框架定位 * 在组件中显示信息 * 处理2D图形 * 使用颜色 * 文本使用特殊字体 * 显示图像 本章主要讲述如何编写定义屏幕 ...

  6. Java多线程编程核心技术-第7章-拾遗增补-读书笔记

    第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...

  7. < 利用Python进行数据分析 - 第2版 > 第五章 pandas入门 读书笔记

    <利用Python进行数据分析·第2版>第五章 pandas入门--基础对象.操作.规则 python引用.浅拷贝.深拷贝 / 视图.副本 视图=引用 副本=浅拷贝/深拷贝 浅拷贝/深拷贝 ...

  8. Javascript模式(第四章函数)------读书笔记

    一 背景 js函数的两个特点:1 函数是第一类对象(first-class object):2 函数可以提供作用域 1 函数是对象: 1 函数可以在运行时动态创建,还可以在程序执行过程中创建 2 可以 ...

  9. Javascript模式(第一章简介)------读书笔记

    一:模式 模式是一个通用问题的解决方案,可以提供一个更好的实践经验.有用的抽象化表示和解决一类问题的模板. 本书主要讨论如下三种类型的模式 1 设计模式:可复用面向对象软件的基础,包括singleto ...

  10. 【我所理解的Cocos2d-x】第六章 精灵Sprite 读书笔记

    简介: 精灵是2D游戏里最重要的元素.游戏场景中大部分可见的元素都直接或间接地与精灵相关. 在Cococs2d-xz中,精灵使用Sprite表示,它将一张纹理的一部分或者全部的矩形区域绘制在屏幕上. ...

随机推荐

  1. python单元测试框架——pytest

    官网:https://docs.pytest.org/en/latest/ pytest帮你写出更好的程序 1.An example of a simple test:(一个简单的例子),命名为tes ...

  2. Linux数据备份与恢复

    Linux数据备份及服务器重要数据类别分析 对 Linux 服务器来讲,当然最理想的就是把整块硬盘中的数据都备份,甚至连分区和文件系统都备份,这样如果硬盘损坏,那么我们可以直接把备份硬盘中的数据导入损 ...

  3. LigerUI ligerComboBox 下拉框 表格 多选无效

    $("#txt1").ligerComboBox({ width: 250, slide: false, selectBoxWidth: 500, selectBoxHeight: ...

  4. JS正则表达式从入门到入土(2)—— 元字符和字符类

    元字符和字符类 元字符 正则表达式由两种基本字符类型组成: 1.原义(正常)文本字符:代表本身含义的字符,如:a.b.c.1.2.3等. 2.元字符:元字符是在正则表达式中有特殊含义的非字母字符,如\ ...

  5. 设置HTML5的video播放速度

    var speed = 1.5;//设置速度 var vdo = document.getElementById("视频的id");//获取id vdo.playbackRate ...

  6. CentOS6.5安装Qt4.8.6+QtCreator2.6.1

    工作中需要用到Qt在Linux下做开发,公司提供的电脑安装的CentOS6.2,但是为了和windows下自己使用的QT版本一直,于是也选择安装了Qt5.1.0.但是在CentOS下刚开始是无法启动, ...

  7. 计算mysql 数据库 表大小 服务器传输 小写表明转成大写

    //数据库表存储大小 select table_schema,table_name,table_rows,concat(round(data_length/1024/1024/1024,2),'GB' ...

  8. 【WPF】修改ComboBox样式

    修改WPF默认的ComboBox控件样式 如下图所示: 修改代码如下: <UserControl.Resources> <Style TargetType="ToggleB ...

  9. 什么是浮动IP

    源地址:https://www.1and1.com/digitalguide/server/know-how/what-is-a-floating-ip/ What is a floating IP? ...

  10. scala中的高阶函数

    版权申明:转载请注明出处. 文章来源:http://bigdataer.net/?p=332 排版乱?请移步原文获得更好阅读体验 1.scala中的函数 scala是一门面向对象和函数式编程相结合的语 ...