13 Use Objects to manage resources

Suppose we are working with a library for modeling investments, there is a root class for different types of investments.

class Investment{...};

And we can get a specific Investment object through a factory function.

Investment * createInvestment();

As for the caller of createInvestment(), it is responsible to delete the object when it is done with it.
Consider, a function f written to fulfill this obligation:

void f()
{
 Investment *pInv = createInvestment();
...
delete pInv;
}

However, f could fail to delete the investments object for some reasons.

There might be a "return" in the
"..." part of the function.

There is a loop in the "..." part of the function, and it executes "continue" or "break", so it cannot execute "delete".

some statement inside "..." throw an exception..

In order to make sure the resource return by createInvestment is always released, we need to put the resource inside an object whose destructor will automatically release the resource when control leaves f.

At most time, resources are dynamically allocated on the heap and used within a single block or function.The standard library's auto_ptr is tailor-made for this kind of situation.auto_ptr is a pointer-like object whose destructor automatically calls delete on what is points to. So we can modify the procedure:

void f()
{
  std::auto_ptr<investment> pInv(createInvestment());
...
}

And it shows two critical aspects of using objects to manage resources:

  • Resources are acquired and immediately turned over to resource-managing objects.

The idea of using objects to manage resources is often called Resource Acquisition is Initialization(RAII)(资源取得时机便是初始化时机),because it is so common to acquire a resource and initialize a resource-managing object in the same statement.

  • Resource-managing objects use their destructors to ensure that resources are released.

Because destructors are called automatically when an object is destroyed(e.g. when an object goes out of scope), resources are correctly released.

Since an auto_ptr automatically deletes what it points to when the auto_ptr is destroyed, it is important that there never be more than one auto_ptr pointing to an object.Because if the object is deleted more than once, it would put the program on the fast track to undefined behavior.In order to prevent this error, auto_ptr has an unusual characteristic : copying them sets them to null, and the copying pointer assumes sole ownership of the resource!

std::auto_ptr<Investment> pInv1(createInvestment());
std::auto_ptr<Investment> pInv2(pInv1); // pInv2 points to the object ,pInv1 is null

pInv1 = pInv2; // pInv1 points to the object , pInv2 is null

It ensures that there would never has two auto_ptr points to the same object.

About the shared_ptr, it is a kind of reference-counting smart pointer(RCSP). An RCSP is a smart pointer that keeps track of how many objects point to a particular resource and automatically deletes the resource when nobody is pointing to it any longer. However, RCSP cannot break cycles of references(e.g., two unused objects that point to each other).

void f()
{
std::tr1::shared_ptr<Investment> pInv1(createInvestment());

std::tr1::shared_ptr<Investment> pInv2(pInv1); // both pInv1 and pInv2 point to the object

pInv1 = pInv2;  // nothing has changed
...
}

auto_ptr and shared_ptr are good examples of using objects to manage resources.
P.S : Both auto_ptr and shared_ptr use delete in their destructors, not delete []. So it is bad idea to use them with allocated arrays.

Conclusion

  • To prevent resource leaks , use RAII objects that acquire resources in their constructors and release them in their destructors.
  • Two commonly useful RAII classes are tr1::shared_ptr and auto_ptr.tr1::shared_ptr is usually the better choice, because its behavior when copied is intuitive.Copy an auto_ptr sets it to null.
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容