您的位置:首页 > 编程语言 > C语言/C++

Effective C++ 改善程序与设计的55个具体做法 二周目笔记03

2015-09-17 15:13 351 查看
第三部分 资源管理

条款13:以对象管理资源

如果在delete之前,程序return或者continue到其他位置或者抛出异常,总之无论delete如何被忽略过去,泄露的不只是内存还有其中保存的一些重要数据。许多资源被动态分配于heap内然后被用于单一区块或函数内。它们应该在控制流离开那个区块或函数时被释放。标准程序库中auto_ptr正是针对这种形势而设计的特制产品。

获得资源后立刻放进管理对象内。实际上“以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”(resource acquisition
is initialization;RAII)

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

由于资源管理对象被销毁时会自动删除它们所管理的(内容)内存。如果多个auto_ptr同时指向同一对象。对象会被删除一次以上,程序报错或者产生未定义行为。为了预防这个问题,auto_ptr通过copy构造函数copy
assignment 操作符
在复制它们的时候会变成null,而复制的指针的将取得资源的唯一所有权。

Auto_ptr的替代方案是“引用计数型智慧 指针(reference-countingsmart pointer;RCSP)”。所谓RCSP是指,持续追踪共有多少对象指针指向某笔资源,并在无人指向它的时候自动删除该资源。

由于STL要求容器内部元素可以正常复制,所以STL容器总不可以存储auto_ptr,亦即类似vector<auto_ptr<int>>some_vector;是禁止的。

RCSP无法逃离的梦靥:环状引用。

Auto_ptr和tr1::shared_ptr两者都在其析构函数内实现delete而不是delete[]动作。那么此时,

 

#include <memory>

std::auto_ptr<int>   api(new int[10]); 

std::shared_ptr<std::string>   sps(new std::string[10]);

销毁api和sps之时便会产生未定义行为。

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

在设计copying行为时,根据需要有四种选项:

禁止复制;

使用引用计数;

深拷贝;

转移所有权。

 另外,shared_ptr允许制定所谓的“删除器“(deleter),那是一个函数或者函数对象,当引用次数为0时,便被调用(此机能并不存在于auto_ptr——它总是将其指针删除)。

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

 APIs往往要求访问原始资源(raw resource),所以每一个RAII
class应该提供一个“取得其所管理只资源“的办法。对于原始资源的访问可能经由显式转换或隐式转换。一般而言,显式转换比较安全,但隐式转换对客户比较方便。两种转换实现一种即可,例如下:

class raw_resources_base_class {};

//根据不同的参数产生不容的原始资源,这里为了方便演示,省略参数

raw_resources_base_class* create_resource(){};

class resources_mangaging_class

{

         resources_mangaging_class(raw_resources_base_class * p) :Praw_reasource(p){}

         operator raw_resources_base_class *() const { return Praw_reasource; }//隐式转换

         raw_resources_base_class * get() const { return Praw_reasource; }//显式转换

         ~resources_mangaging_class(){ delete Praw_reasource; }

private:

         raw_resources_base_class *Praw_reasource;

};

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

即将被删除的的那个指针,所指的是单一对象还是对象数组?这是个比不可却的问题,因为单一对象的内存布局一般而言不同于数组的内存布局。更明确地说,数组所用的内存通常还包括“数组大小“的记录,以便delete知道需要调用多少次析构函数。单一对象的内存则没有这笔记录。你可以把两种不同的内存布局想象如下,其中n是数组大小:





虽然很多编译器都是这么实现的,但是编译器并不需非得这么实现不可,这只是一个例子。

无论是对new使用delete[ ]还是对new[]使用delete,所引发的结果都是未定义的。所以,成对使用new和delete时要采取相同的形式

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

int priority();

void process_something(std::shared_ptr<int> one, int priority);

 

process_something(std::shared_ptr<int>(new int), priority());

//编译器产生process_something调用码之前,必须核算即将被传递的各个实参。

//于是在调用process_something之前,必须创建代码,做一下三件事情:

//调用priority()

//执行“new int”

//调用share_ptr构造函数

 

编译器选择什么样的顺序完成这些事情的弹性很大。如果是以下次序(说不定会产生更高效的代码):

执行“new int”;

调用priority();

调用share_ptr构造函数。

如果,priority()调用导致异常,在此情况下,“new int“返回的指针将会遗失,造成资源泄露。由于编译器对于”跨越语句的各项操作“没有重新排列的自由(只有在语句内它才拥有那个自由度)。因此可做一下调用的修改:

std::shared_ptr<int> Pi(new int);

process_something(Pi, priority());

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++