Item 13 - Use objects to manage resources.
Suppose we're working with a library for modeling investments (e.g., stocks, bonds, etc.), where the various investment types inherit from a root class Investment
:
Further suppose that way the library provides us with Investment
objects is through a factory function
A function that returns a base class pointer to a newly-created derived class object.
As the comment indicates, callers of createInvestment
are responsible for deleting the object that function returns when they are done with it. Consider, then, a function f
written to fulfill this obligation:
This looks okay, but there are several ways f
could fail to delete the investment object it gets from createInvestment
. There might be a premature return
statement somewhere inside the "..." part of the function. If such a return
were executed, control would never reach the delete
statement. A similar situation would arise if the uses of createInvestment
and delete
were in a loop, and loop was prematurely exited by a break
or goto
statement. Finally, some statement inside the "..." might throw an exception. If so, control would again not get to the delete
.Regardless of how the delete
were to be skipped, we'd leak not only the memory containing the investment object but also any resources help by that object.
Many resources are dynamically allocated on the heap, are used only within a single block or function, and should be released when control leaves that block or function. The standard library's auto_ptr
is tailor-made for this kind of situation. auto_ptr
is a pointer-like object (a smart pointer) whose destructor automatically calls delete
on what it points to. Here's how to use auto_ptr
to prevent f
's potential resource leak:
This simple example demonstrates the two critical aspects of using objects to manage resources:
Resources are acquired and immediately turned over to resource-managing objects. Above, the resource returned by
createInvestment
is used to initialize theauto_ptr
that will manage it. In fact, the idea of using objects to manage resources is often called Resource Acquisition Is Initialization (RAII), because it's so common to acquire a resource and initialize a resource-managing object the same statement. Sometimes acquired resources are assigned to resource-managing objects instead of initializing them, but either way, every resource is immediately turned over to a resource-managing object at the time the resource is acquired.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, regardless of how control leaves a block.
Because an auto_ptr
automatically deletes what it points to when auto_ptr
is destroyed, it's important that there never be more than one auto_ptr
pointing to an object. If there were, the object would be deleted more than once, and that would put your program on the fast tract to undefined behavior. To prevent such problems, auto_ptr
have an unusual characteristic: copying them (via copy constructor or copy assignment operator) sets them to null
and the copying pointer assumes sole ownership of the resource!
STL containers require that their contents exhibit "normal" copying behavior, so containers of auto_ptr
aren't allowed.
An alternative to auto_ptr
is a 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. As such, RCSPs offer behavior that is similar to that of garbage collection. Unlike garbage collection, however, RCSPs can't break cycles of references (e.g., two otherwise unused objects that point to one another). shared_ptr
is an RCSP, so you could write f
this way:
This code looks almost the same as that employing auto_ptr
, but copying shared_ptr
behaves much more naturally:
Because shared_ptr
works "as expected", they can be used in STL containers, and other contexts where autp_ptr
unothodox copying behavior is inappropriate.
Both auto_ptr
and shared_ptr
use delete
in their destructors, not delete[]
. That means that using auto_ptr
or shared_ptr
with dynamically allocated arrays is a bad idea, though, one that will compile:
There is noting like auto_ptr
or shared_ptr
for dynamically allocated arrays in C++, that's because vector
and string
can almost always replace dynamically allocated arrays.
Things to Remember
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
shared_ptr
andauto_ptr
.shared_ptr
is usually the better choice, because its behavior when copied is intuitive. Copying anauto_ptr
sets it to null.
Last updated