C 中的健壮指针和资源管理

2008-02-23 05:25:08来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

 我最喜欢的对资源的定义是:"任何在您的程式中获得并在此后释放的东西。"内存是个相当明显的资源的例子。他需要用new来获得,用delete来释放。同时也有许多其他类型的资源文档句柄、重要的片断、Windows中的GDI资源,等等。将资源的概念推广到程式中创建、释放的任何对象也是十分方便的,无论对象是在堆中分配的还是在栈中或是在全局作用于内生命的。

  我最喜欢的对资源的定义是:"任何在您的程式中获得并在此后释放的东西。"内存是个相当明显的资源的例子。他需要用new来获得,用delete来释放。同时也有许多其他类型的资源文档句柄、重要的片断、Windows中的GDI资源,等等。将资源的概念推广到程式中创建、释放的任何对象也是十分方便的,无论对象是在堆中分配的还是在栈中或是在全局作用于内生命的。

  C 中的健壮指针和资源管理  

  资源及他们的任何权

  我最喜欢的对资源的定义是:"任何在您的程式中获得并在此后释放的东西?quot;内存是个相当明显的资源的例子。他需要用new来获得,用delete来释放。同时也有许多其他类型的资源文档句柄、重要的片断、Windows中的GDI资源,等等。将资源的概念推广到程式中创建、释放的任何对象也是十分方便的,无论对象是在堆中分配的还是在栈中或是在全局作用于内生命的。

  对于给定的资源的拥有着,是负责释放资源的一个对象或是一段代码。任何权分立为两种级别--自动的和显式的(automatic and explicit),假如一个对象的释放是由语言本身的机制来确保的,这个对象的就是被自动地任何。例如,一个嵌入在其他对象中的对象,他的清除需要其他对象来在清除的时候确保。外面的对象被看作嵌入类的任何者。

  类似地,每个在栈上创建的对象(作为自动变量)的释放(破坏)是在控制流离开了对象被定义的作用域的时候确保的。这种情况下,作用于被看作是对象的任何者。注意任何的自动任何权都是和语言的其他机制相容的,包括异常。无论是如何退出作用域的--正常流程控制退出、一个break语句、一个return、一个goto、或是个throw--自动资源都能够被清除。

  到现在为止,一切都很好!问题是在引入指针、句柄和抽象的时候产生的。假如通过一个指针访问一个对象的话,比如对象在堆中分配,C 不自动地关注他的释放。程式员必须明确的用适当的程式方法来释放这些资源。比如说,假如一个对象是通过调用new来创建的,他需要用delete来回收。一个文档是用CreateFile(Win32 API)打开的,他需要用CloseHandle来关闭。用EnterCritialSection进入的临界区(Critical Section)需要LeaveCriticalSection退出,等等。一个"裸"指针,文档句柄,或临界区状态没有任何者来确保他们的最终释放。基本的资源管理的前提就是确保每个资源都有他们的任何者。

  第一规则

  一个指针,一个句柄,一个临界区状态只有在我们将他们封装入对象的时候才会拥有任何者。这就是我们的第一规则:在构造函数中分配资源,在析构函数中释放资源。

  当您按照规则将任何资源封装的时候,您能够确保您的程式中没有任何的资源泄露。这点在当封装对象(Encapsulating Object)在栈中建立或嵌入在其他的对象中的时候很明显。但是对那些动态申请的对象呢?不要急!任何动态申请的东西都被看作一种资源,并且要按照上面提到的方法进行封装。这一对象封装对象的链不得不在某个地方终止。他最终终止在最高级的任何者,自动的或是静态的。这些分别是对离开作用域或程式时释放资源的确保。

  下面是资源封装的一个经典例子。在一个多线程的应用程式中,线程之间共享对象的问题是通过用这样一个对象联系临界区来解决的。每一个需要访问共享资源的客户需要获得临界区。例如,这可能是Win32下临界区的实现方法。

class CritSect
{
friend class Lock;
public:
CritSect () { InitializeCriticalSection (&_critSection); }
~CritSect () { DeleteCriticalSection (&_critSection); }
private
void Acquire ()
{
EnterCriticalSection (&_critSection);
}
void Release ()
{
LeaveCriticalSection (&_critSection);
}

CRITICAL_SECTION _critSection;
};

  这里聪明的部分是我们确保每一个进入临界区的客户最后都能够离开。"进入"临界区的状态是一种资源,并应当被封装。封装器通常被称作一个锁(lock)。

class Lock
{
public:
Lock (CritSect& critSect)
: _critSect (critSect)
{
_critSect.Acquire ();
}
~Lock ()
{
_critSect.Release ();
}
private
CritSect & _critSect;
};
锁一般的用法如下:
void Shared::Act () throw (char *)

Lock lock (_critSect);
// perform action -- may throw
// automatic destructor of lock
}

  注意无论发生什么,临界区都会借助于语言的机制确保释放。

  更有一件需要记住的事情--每一种资源都需要被分别封装。这是因为资源分配是个很容易出错的操作,是要资源是有限提供的。我们会假设一个失败的资源分配会导致一个异常--事实上,这会经常的发生。所以假如您想试图用一个石头打两只鸟的话,或在一个构造函数中申请两种形式的资源,您可能就会陷入麻烦。只要想想在一种资源分配成功但另一种失败抛出异常时会发生什么。因为构造函数还没有全部完成,析构函数不可能被调用,第一种资源就会发生泄露。

  这种情况能够很简单的避免。无论何时您有一个需要两种以上资源的类时,写两个笑的封装器将他们嵌入您的类中。每一个嵌入的构造都能够确保删除,即使包装类没有构造完成。



[1]

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: C 运算符重载探讨

下一篇: 用Builder C 设计串行口COM1或COM2的读写操作