effective c++:资源管理

对象管理资源

createInvestment 函数作用时创建一个invest对象:

void f()
{
  Investment *pInv = createInvestment(); // call factory function
  ... // use pInv
  delete pInv; // release object
}

既然f函数中创建对象,销毁对象的责任也应该由它来承担,但是如果“……”区域出现异常或return提前执行,delete将被跳过,从而造成严重的内存泄露,为了确保无论发生什么情况createInvestment返回的对象始终能记得被销毁,我们就需要将他放入一个对象内,析构函数会自动的释放资源,c++标准库提供auto_ptr,即智能指针,他是个类指针对象,析构函数自动对他调用delete:

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时要注意,一旦让多个auto_ptr指向同一个对象,资源只存放在最后指向的指针中,其他指针均为null,所以auto_ptr在实际使用时局限性很大,c++11已经舍弃了这一智能指针,他的替代方案时shared_ptr,它能够持续的跟踪共有多少个对象指向资源,在无人指向资源时才删除该资源,但是由于shared_ptr在析构函数中使用的是delete而不是delete[],对于动态分配的数组显然不可取,针对动态数组的智能指针在c++11中并没有提供,但在幸运的事boost中有我们想要的boost::scoped_array和boost::shared_array,google c++ sytle中建议需要使用智能指针的话尽量使用scoped_ptr,只在非常特定的情况下使用 std::tr1::shared_ptr, 例如 STL 容器中的对象。“智能” 指针看上去是指针, 其实是附加了语义的对象. 以 scoped_ptr 为例, scoped_ptr 被销毁时, 它会删除所指向的对象. shared_ptr 也是如此, 并且 shared_ptr 实现了引用计数, 所以最后一个 shared_ptr 对象析构时, 如果检测到引用次数为 0,就会销毁所指向的对象。一般来说,我们倾向于设计对象隶属明确的代码, 最明确的对象隶属是根本不使用指针, 直接将对象作为一个作用域或局部变量使用. 另一种极端做法是, 引用计数指针不属于任何对象. 这种方法的问题是容易导致循环引用, 或者导致某个对象无法删除的诡异状态, 而且在每一次拷贝或赋值时连原子操作都会很慢。

假如将shared_ptr用在互斥器中(mutex objects)

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

shared_ptr缺省时将引用次数为0时删除所指物,然而在Mutex中,我们要做的释放动作时解锁而不是删除,这就要用到shared_ptr中的删除器来指定代替默认状态下的删除操作,本例中引用计数为0时则自动调用unlock函数

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 Item15 for info on “get”
  }
private:
  std::tr1::shared_ptr<Mutex> mutexPtr; // use shared_ptr
};

new与智能指针

假设我们有一个函数来解释处理程序的优先权,另一个函数用来动态分配Widge上进行某些优先权处理:

int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);

processWidge决定动态分配得来的Widge运用智能指针

processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());

虽然这里使用了智能指针,但还是会有泄露的发生。

实际参数("std::tr1::shared_ptr<Widget>(new Widget)"),由两部分组成:new Widge表达式和shared_ptr构造函数,但是c++编译器的运行次序可不是按照参数次序来执行,对于priority的调用可能会在new Widge表达式和shared_ptr构造函数两个操作的之前,中间,最后执行,问题在于如果非常不巧的priority在第一个实参的两个操作之间执行,又非常不幸priority调用时抛出异常:

1. Execute “new Widget”.
2. Call priority.
3. Call the tr1::shared_ptr constructor.

 第一个new操作的建立 的资源将没有办法回收。

避免这一问题的办法是分离语句,明确调用顺序

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

 

原文地址:https://www.cnblogs.com/loujiayu/p/3602723.html