(Effective C++)第三章 资源管理(Resource Management)

来源:互联网 发布:软件代替身份证读卡器 编辑:程序博客网 时间:2024/06/05 01:23

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());