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

(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());
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息