细读《Effective C++》之三

Chapter 3. Resource Management

Scott说:这儿的resource包括dynamically allocated memory、file descriptors、mutex locks、GUI objects、database connections和network sockets。

“拿了我的给我还回来,吃了我的给我吐出来”……

Item 13 - 17

条款13:Use objects to manage resources
void  f()
{
  Investment 
*pInv = createInvestment();         // call factory function
  ...                                            // use pInv
  delete pInv;                                   // release object
}

为了防止程序在delete之前return或因exception中断而无法调用delete,可使用smart pointer auto_ptr(a pointer-like object, its destructor automatically calls delete on what it points to):

//  Resource Acquisition Is Initialization(RAII)
void  f()
{
  std::auto_ptr
<Investment> pInv(createInvestment());  // call factory function
  ...                                                  // use pInv as before
}
                                                       //  automatically delete pInv via auto_ptr's dtor

auto_ptr的初始化(也可以用assignment)和resource的分配在同一条语句中完成,其destructor保证了资源会被release。为了保证在任意时刻只有一个auto_ptr指向同一个资源(这也是其缺陷),当一个auto_ptr被copy给另一auto_ptr对象之后,前一个将被置为null,这很安全,却也很麻烦。所以在使用auto_ptr指针时要注意copy操作。

如果希望多个指针指向同一资源,可使用引用计数智能指针(reference-counting smart pointer, RCSP)TR1的tr1::shared_ptr指针。

auto_ptr和tr1::shared_ptr都不能用于动态分配数组,因为二者都没有delete[]的用法。boost::scoped_array和boost::shared_array可以分配数组。

条款14:Think carefully about copying behavior in resource-managing classes

条款13主要针对heap-based的资源进行管理,不是所有资源都是heap-based,如mutex。

void   lock (Mutex  * pm);                //  lock mutex pointed to by pm
void  unlock(Mutex  * pm);              //  unlock the mutex

借用条款13的RAII思想,设计class Lock:

class  Lock :  private  Uncopyable                 //  prohibit copying
{
public:
  
explicit Lock(Mutex *pm) : mutexPtr(pm)
  
lock(mutexPtr); }                          // acquire resource
  ~Lock() { unlock(mutexPtr); }                // release resource

private:
  Mutex 
*mutexPtr;
}
;

对于申请类似critical resources,同样可借用RCSP的思想实现,对critical resources的申请和分配进行计数。这里的一个问题是在tr1::shared_ptr析构时如果counter为0将delete掉mutex,因此可以使用function object实现如下:

class  Lock  {
public:
  
explicit Lock(Mutex *pm)       // init shared_ptr with the Mutex
  : mutexPtr(pm, unlock)         // to point to and the unlock func
  {                              // as the deleter
    lock(mutexPtr.get());        // see Item 15 for info on "get"
                                 
// no longer declares a destructor
  }


private:
  std::tr1::shared_ptr
<Mutex> mutexPtr;    // use shared_ptr
}
;                                          //  instead of raw pointer
条款15:Provide access to raw resources in resource-managing classes

为了能使资源顺利释放,Scott提倡我们使用auto_ptr和tr1::shared_ptr,友情提示:智能指针auto_ptr和tr1::shared_ptr不是指针,而是pointer-like object。因此如果要访问原始资源,需要从其中取出。

std::tr1::shared_ptr < Investment >  pInv(createInvestment());   //  from Item 13
int  daysHeld( const  Investment  * pi);         //  return number of days investment has been held
int  days  =  daysHeld(pInv. get ());            //  fine, passes the raw pointer in pInv to daysHeld

说白了,使用auto_ptr和tr1::shared_ptr时,记得它们是对象就行了。

犹如在使用string时,为了得到字符串,你需要这样去用:

string  str( " Hello " );
char   * pstr  =  str.c_str();

除了类似的explicit convertion,也有implicit convertion:

class  Font  {
public:
  ...
  
operator FontHandle() const return f; }      // implicit conversion function
  ...
}
;

Font f(getFont());
int  newFontSize;
...
changeFontSize(f, newFontSize);                  
//  implicitly convert Font to FontHandle

诚如Scott所言:The best design is likely to be the one that adheres to make interfaces easy to use correctly and hard to use incorrectly.

至于对原始资源访问所造成的contrary to encapsulation,则可以通过design使client取其所取,避其所避。

对于C/C++程序员来讲,诸如“{”和“}”,“[”和“]”,“"”和“"”,Get和Release,malloc和free,new和delete这样的搭配我们早已习惯,而对于借助对象的constructor和destructor来完成资源“自动”的分配和释放这样的安全操作显然还需要时间去适应。

看到这一款,我越来越觉得我的C++基础需要进一步加强。不知道Scott新加的这第三章内容是过于艰涩,还是怎么,总觉得似乎简单,但又没有那么简单,理解的不够深刻。

条款16:Use the same form in corresponding uses of new and delete

这一款在第二版时也是有的,第三版还是加了点内容(尽管不多,却让内容更加易于理解),首先对Scott的态度表示尊敬。

new和delete不仅仅是要成对出现的问题(当然,这是最根本最重要的问题),而更是要正确的成对出现,就像{}一样。当new被使用时,发生了两件事:内存分配和constructor(s)调用。当delete被使用时,也发生了两件事:destructor(s)调用和内存回收。问题是你用了什么就要还回什么,你用了多少就要还回多少。

因此,Scott说:“如果你调用new时用了[],调用delete时也要用[]。如果调用new时没有用[],那调用delete时也不要用[]”。

typedef  string  addresslines[ 4 ]; 

string   * pal  =   new  addresslines;   //  这个typedef使用的太有创意了!
delete pal;                                         //  错误!
delete [] pal;                                      //  正确
条款17:Store newed objects in smart pointers in standalone statements
int  priority();
void  processWidget(std::tr1::shared_ptr < Widget >  pw,  int  priority);

//  tr1::shared_ptr's constructor taking a raw pointer is explicit
processWidget( new  Widget, priority());                                        //  error!
processWidget(std::tr1::shared_ptr < Widget > ( new  Widget), priority());          //  OK!

对于初次看到Scott的Resource Management这一章的coders,我不知道大家看过之后能领会多少,反正我自己心里没有底,各种智能指针的使用,想必也和个人的智商成一定正比。总觉得看过之后,再去写代码,难保不是邯郸学步。

这时候,Scott又说:although we're using object-managing resources everywhere here, this call may leak resources……

Before processWidget can be called, then, compilers must generate code to do these three things:

1) Call priority.

2) Execute "new Widget".

3) Call the tr1::shared_ptr constructor.

然而,the call to priority can be performed first, second, or third. 如果不幸的成了这样:

1) Execute "new Widget".

2) Call priority.

3) Call the tr1::shared_ptr constructor.

如果更加不幸的是:the call to priority yields an exception。不要因为这种可能只有不到0.01%,如果被你的客户和老板抓到,那就是100%。

std::tr1::shared_ptr < Widget >  pw( new  Widget);   //  store newed object in a smart pointer in a standalone statement
processWidget(pw, priority());                 //  this call won't leak

Scott建议:Store newed objects in smart pointers in standalone statements. Failure to do this can lead to subtle resource leaks when exceptions are thrown.

粗略地看完这一章,Scott带我们实现了Resources Management:

1) Use objects(smart pointers objects) to manage resources: auto_ptr, tr1::shared_ptr and boost::shared_array;

2) Think carefully about copying behavior in resource-managing classes: prohibit copying or reference-count, delete or unlock;

3) Provide access to raw resources in resource-managing classes: explicit or implicit, safer or more convenient;

4) Use the same form in corresponding uses of new and delete: new and delete, new [] and delete [];

5) Store newed objects in smart pointers in standalone statements: avoid exception thrown between new and copy to tr1::shared_ptr.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值