条款13:以对象管理资源
1、借助析构函数释放资源
看一个简单的例子
class Product
{
public:
enum {CPU, KEY};
};
class CPU : public Product {};
class KEY : public Product {};
Product *GetProduct(const int type)
{
switch (type)
{
case Product::CPU: return new CPU();
case Product::KEY: return new KEY();
default: return NULL;
}
}
调用
void call()
{
Product *p = GetProduct(1);
//operator
delete p;
}
GetProduct()获得的对象资源是需要自己手工释放的,后面也调用了delete,一切都是循序渐进,假如有以下情况则delete不能执行
①在对象操作中抛出异常
②在delete之前执行了return、goto语句
③GetProduct()在循环里面执行,并执行continue语句退出
①在对象操作中抛出异常
②在delete之前执行了return、goto语句
③GetProduct()在循环里面执行,并执行continue语句退出
为了确保GetProduct()获得的资源能够总是被释放,做法是将资源放进对象内,在析构函数中执行释放资源动作,当对象离开控制域后,会自动执行析构函数.
2、智能指针
智能指针实际是一种类对象指针,实现原理是将资源放到对象里面,在构造函数中获取对象资源并初始化,在析构函数中delete掉该资源。
auto_ptr
通过auto_ptr修改上面调用
void call()
{
std::auto_ptr<Product> ptr(GetProduct(1)); //将资源放到智能指针中
//operator
} //离开作用域,自动delete掉资源
以对象管理资源的两个关键点
获得资源后立刻放进管理对象内
实际上“以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”,因为我们几乎总是在获得一笔资源后于同一语句内以它初始化某个管理对象。有时候获得的资源被拿来赋值(而非初始化)某个管理对象,但不论哪一种做法,每一笔资源都在获得的同时立刻被放进管理对象中。
管理对象运用析构函数确保资源被释放
不论控制流如何离开区块,一旦对象被销毁(例如当对象离开作用域)其析构函数自然会被自动调用,于是资源被释放。如果资源释放动作可能导致抛出异常,事情变得有点棘手,但条款8已经能够解决这个问题,所以这里我们就不多操心了。
实际上“以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”,因为我们几乎总是在获得一笔资源后于同一语句内以它初始化某个管理对象。有时候获得的资源被拿来赋值(而非初始化)某个管理对象,但不论哪一种做法,每一笔资源都在获得的同时立刻被放进管理对象中。
管理对象运用析构函数确保资源被释放
不论控制流如何离开区块,一旦对象被销毁(例如当对象离开作用域)其析构函数自然会被自动调用,于是资源被释放。如果资源释放动作可能导致抛出异常,事情变得有点棘手,但条款8已经能够解决这个问题,所以这里我们就不多操心了。
auto_ptr被销毁会自动删除它所指之物,如果用多个auto_ptr指向同一个对象,则多个对象会被删除多次,为了防止这种情
况,auto_ptr有个实现机制,若通过copy函数复制他们,他们就会变为null,而复制所得的指针将得到资源的唯一拥有权。
std::auto_ptr<Product> ptr(GetProduct(1)); //将资源放到智能指针中
std::auto_ptr<Product> ptr2(ptr); //ptr2指向对象,ptr为null
ptr = ptr2; //ptr指向对象,ptr2为null
RCSP智能指针---shared_ptr
RCSP(reference-counting smart pointer),是一种引用计数智能指针,追踪多个对象指向资源,在无人使用时自动释放资源。
将上面的代码修改如下
void call()
{
std::tr1::shared_ptr<Product> ptr(GetProduct(1)); //将资源放到智能指针中,引用计数为1
std::tr1::shared_ptr<Product> ptr2(ptr); //ptr2指向对象,引用计数为2
ptr = ptr2; //无变化
} //跳出作用域,引用计数为0,自动执行对象析构函数释放资源
auto_ptr和shared_ptr两者在底层析构函数的实现都是通过delete而不是delete[],因此把数组应用到两者中会出出问题。不过对于数组,可以通过string和vector实现。
对于有些系统资源不是通过new和delete来管理的,我们可以自己实现对象管理类,后面的条款会说到。
记住
1、为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
2、两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null。
2、两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null。