(Effective C++)第三章 资源管理(Resource Management)
2011-10-15 19:57
330 查看
5.1 条款13:以对象管理资源(Use object to manage resource)
以对象管理资源。一般情况下,谁申请资源,谁负责释放。如下:
class Investment {};
void f
{
Investment *pInv = new Investment(); //调用创建类
…
delete pInv; //释放资源
}
为了确保申请的资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资源。实际上,这是依赖C++析构函数自动调用机制确保资源被释放。
void f()
{
std::auto_ptr<Investment> pInv(new Investment()); //经由auto_ptr的析构函数自动删除pInv
…
}
auto_ptr是智能指针,是管理对象,运用析构函数确保资源被释放。由于auto_ptr被销毁时会自动删除它所指之物,所以一定要注意别让多个auto_ptr同时指向同一个对象。auto_ptrs有一个不寻常的性质,若通过copy构造函数或copy assignment操作符赋值他们,它们会变成NULL,而复制所得指针将取得资源的唯一拥有权。
std::auto_ptr<Investment> pInv1(new Investment()); //
std::auto_ptr<Investment> pInv2(pInv1); //现在PInv2指向对象,而pInv1被设为NULL
pInv1 = PInv2; //现在PInv1指向对象,而pInv2被设为NULL
示例5-1-1 auto_ptr的copying函数的行为
注入,STL容器要求其元素发挥正常的复制行为,因此这些容器容不得auto_ptr。
auto_ptr的替代方案是引用计数型智慧指针,也是智能指针。
void f()
{
//经由shared_ptr的析构函数自动删除pInv
std::tr1::shared_ptr<Investment> pInv(new Investment());
…
}
shared_ptr的复制行为
std:: tr1::shared_ptr<Investment> pInv1(new Investment()); //
std:: tr1::shared_ptr<Investment> pInv2(pInv1); //现在pInv1和PInv2指向同一个对象
pInv1 = PInv2; //同上,无任何改变
示例5-1-1 shared_ptr的copying函数的行为
注意事项
auto_ptr和tr1::shared_ptr两者都是在其析构函数内做delete而不是做delete[]动作。
5.2 条款14:在资源管理类中小心copying行为(Think carefully about copying behavior in resource-managing classes)
在资源管理类中小心copying行为。如果需要设计一个类来管理资源,其基本结构由RAII(资源取得时机便是初始化时机)守则支配,也就是“资源在构造期间获得,在析构期间释放。“
class Lock
{
public:
explicit Lock(Mutex & pm):mutexPtr(pm)
{
lock(mutexPtr);
}
~Lock() {unlock(mutexPtr);} //释放资源
private:
Mutex *mutexPtr;
};
Mutex m; //定义你需要的互斥器
…
{ //建立一个区块用来定义临界区
Lock m1(&m); //加锁
… //执行临界区操作
} //在区块最末尾,自动解除互斥器锁定
//如果Lock对象被复制
Lock m1(&m); //锁定
Lock m2(m1); //复制
示例5-2-1 一份异常安全且高效的operator=实现
当一个RAII对象被复制,一般情况会做如下两个可能:
禁止复制
例如
class Lock: private Uncopyable //禁止复制
{
public:
… // 如前
};
对底层资源使用引用计数法。
5.3 条款15:在资源管理类中提供对原始资源的访问(Provide access to raw resource in resource-managing classes)
假设有这样一个例子:使用智能指针如auto_ptr和tr1::shared_ptr保存new Investment()的调用结果。std::tr1::shared_ptr<Investment> pInv(new Investment ());
但是你希望以某个函数处理Investment对象,想这样:
int daysHeld(const Investment *pi); //返回投资天数
int days = daysHeld(pInv); //错误
因为daysHeld需要的是Investment*指针,你传给它的却是个类型std::tr1::shared_ptr<Investment>的对象。但是auto_ptr和tr1::shared_ptr都提供了一个get成员函数,用来执行显示转换,也就是他会返回智能指针内部的原始指针:
int days = daysHeld(pInv.get()); //正确
就像所有的智能指针一样,auto_ptr和tr1::shared_ptr也重载了指针取值(pointer dereferencing)操作符(operator->和operator*),它们允许转换至底部原始指针。
class Investment
{
public:
bool isTaxFree() const;
};
std::tr1::shared_ptr<Investment> pi1(new Investment ());
bool taxable1 = !(pi1->isTaxFree())
//经由operator->访问资源
std::auto_ptr<Investment> pi2(new Investment ());
bool taxable2 = !((*pi2).isTaxFree())
//经由operator*访问资源
示例5-3-1 智能指针重载操作符(operator->和operator*)
对于原始资源的访问可能经由显示转换或隐式转换。但是显式转换比较安全。
5.4 条款16:成对使用new和delete时要采用相同形式(Use the same form in corresponding uses of new and delete)
略过。见《Effective C++》中文 第三版 P73.5.5 条款17:以独立语句将newed对象置于智能指针(Store newed objects in smart pointers in standalone statement)
有这样的两个方法,如下:int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
像如下这样使用:
processWidget(std::tr1::shared_ptr<Widget> (new Widget), priority());
上述调用可能泄露资源,编译器产生一个processWidget调用码之前,必须先核算即被传递的各个实参。而第一个实参由两部分组成:
1) 执行“new Widget“表达式
2) 调用std::tr1::shared_ptr构造函数
于是,在调用processWidget之前,编译器先做三件事:
1) 调用priority()
2) 执行“new Widget“
3) 调用std::tr1::shared_ptr构造函数
C++编译器以什么样的顺序完成上述三步是不确定。如果出现如下次序:
1) 执行“new Widget“
2) 调用priority()
3) 调用std::tr1::shared_ptr构造函数
如果调用priority()出现失败,而new Widget成功,但是还没来得及置于std::tr1::shared_ptr,所以这样就会造成资源泄露。
正确处理方法:
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());
相关文章推荐
- 信息资源管理第三章——初识系统
- 信息资源管理第三章——初识系统
- Effective c++ 第三章 (资源管理)
- Effective C++第三章-资源管理-1
- 信息资源管理第三章——初识系统
- 信息资源管理第三章——初识系统
- 信息资源管理第三章——初识系统
- 信息资源管理第三章——初识系统
- 第三章 资源管理
- 第三章信息系统资源管理
- 《Effective C++》第三章:资源管理
- 第三章信息系统资源管理
- 第三章 资源管理
- Effective C++第三章-资源管理-2
- 信息资源管理第三章——初识系统
- RBAC新解:基于资源的权限管理(Resource-Based Access Control)
- 如何做RCP里的图片资源管理
- Winsock服务器内存资源管理
- Rails的静态资源管理(五)—— 自定义 Asset Pipeline
- 第三章 实例管理