Using auto_ptr to Handle Memory

The auto_ptr template class is designed to help manage memory in a semi-automatic way and prevent memory leaks when unexpected events such as exceptions would otherwise have caused the normal cleanup code to be skipped.



For instance, it is common to write code such as
void myFunction()
{
    myClass = new myClass();
    // body of function
    delete myClass;
}
and this may work. But what if somewhere in the function body an exception gets thrown? Suddenly, the delete code never gets called! What's more, you may never have intended to throw an exception into the function but one of the functions you call may do so, or you may later need to modify your code to do so. In both cases, this is a memory leak waiting to happen.

On the other hand, by letting the auto_ptr class manage your memory for you, as soon as an exception gets thrown and the auto_ptr you declared has gone out of scope, the memory allocated will automatically be freed. First, let's look at how to use auto_ptr, and then I'll explain the exact mechanics of how this works.

To take advantage of auto_ptr, you will need to include <memory>. This will give you access to the std namespace, in which resides the templated class auto_ptr<type>. For type, you should put the type you want your pointer to point to. For instance, if you would ordinarily have declared an int*, you should use int for the type. When you actually declare an instance of the templated class, the constructor should take a pointer to the specified type. The object will then take care of managing the memory associated with that pointer. For instance, to create an auto_ptr object that manages the memory of a pointer to an integer, you could use this declaration:
std::auto_ptr<int> int_memory_manager(new int);
// Alternately, you could include "using namespace std;" or 
// "using namespace std::auto_ptr" at the top of your code to avoid having to
// prefix all declarations with std::auto_ptr
To actually use the pointer stored in the auto_ptr container, you can just treat the auto_ptr object you create as though it were the pointer. For instance, you can dereference it:
*int_memory_manager = 5;
and if you wanted to access a member function (or variable) of a struct or class, you can simply use the same arrow operator as normal. I'll use a fictitious class called "myClass" to illustrate this.
std::auto_ptr<myClass> myClassManager(new myClass);
myClassManager->variable = 50;
which sets a field called variable to 50 in the instance of myClass whose pointer is stored in myClassManager.

The great benefit of all of this is that you simply don't need to worry about calling delete at all! As soon as int_memory_manager goes out of scope, its destructor will be invoked. Its destructor, in turn, will call delete on the pointer. This brings up a subtle point: you must use auto_ptr with memory allocated with new, not malloc, and, moreover, not new[]. Each of these memory allocators must be properly paired: malloc with free, new with delete, and new[] with delete[] (new[] is used to allocate arrays). If they aren't matched correctly, your program may crash.

The consequence of auto_ptr's always calling delete on the pointer it stores means that you can't store arrays in an auto_ptr. For instance, the following is a mistake:
std::auto_ptr<int> int_memory_manager(new int[10]);
What happens when int_memory_manager goes out of scope? The program invokes delete on an array, and this is illegal. Some compilers may let you get away with it, but it's not portable. Just don't do it! If you really need to store a collection of items with constant random access times, just use a vector from the Standard Template Library (STL).

If you just need to retrieve the address of the pointer held in an auto_ptr object, you can use the get method. The following code demonstrates a function that returns a pointer to an integer as stored in an auto_ptr object:
int* example()
{
std::auto_ptr<int> int_memory_manager(new int);
return int_memory_manager.release(); // we'll see release in more depth below 
Of course, you'd probably be better off simply returning an auto_ptr object in the first place.

Some Caveats

While you can assign one auto_ptr object to another, when you do so, the actual assignment results in the transferral of the pointer from one object to the other.

For instance,
std::auto_ptr<int> int_memory_manager(new int);
std::auto_ptr<int> int_memory_manager2;
cout<<"Contents of first is "<<int_memory_manager.get()<<endl;
int_memory_manager2 = int_memory_manager;
cout<<"Contents of first is "<<int_memory_manager.get()<<endl;
cout<<"Contents of second is "<<int_memory_manager2.get()<<endl;
This sample program demonstrates that the overloaded copy operator for auto_ptr actually removes the pointer from the object being copied! It's set to NULL. This means that only one auto_ptr object can hold a pointer at any time. The act of assigning one auto_ptr to another changes the auto_ptr being assigned. Moreover, this situation holds true even when implicitly copying auto_ptr objects -- for instance, if you make a function call and pass an auto_ptr object, when the function returns, the contents of auto_ptr will be changed to NULL, as demonstrated by the following code:
using namespace std;
void aFunction(std::auto_ptr<int> x)
{
}

int main()
{
    std::auto_ptr<int> int_manager(new int);
    aFunction(int_manager);
    cout<<"Content of int_manager is "int_manager.get()<<endl;
    // Expected output: 0
}
In truth, this behavior is quite beneficial because it means that two auto_ptr objects will not both try to delete the same pointer. Deleting the same pointer twice is not a valid operation, and can lead to program crashes, whereas deleting NULL is a valid operation (though it doesn't change anything).

A subtle consequence of this behavior is that auto_ptrs don't work well in all scenarios. For instance, using auto_ptr objects with the standard template library can lead to problems as some functions in the STL may make copies of the objects in containers such as the vector container class. One example is the sort function, which makes copies of some of the objects in the container being sorted. As a consequence, this copy can blithely delete the data in the container!

If for some reason you did want to store an auto_ptr object in an STL container, you should probably either write your own memory manager container that counts the number of references to a pointer or handle freeing the memory somewhere else. If you wanted to take the second approach, you'll need to be able to tell an auto_ptr object that you're finished using it to manage your pointer. You can do this with the release function. For instance, the following code requests that an auto_ptr object allow us to handle the memory for the object again and resets the pointer stored by the auto_ptr object to NULL so that it doesn't get deleted twice.
std::auto_ptr<int> int_memory_manager(new int);
// Calling release sets the pointer managed by int_memory_manager to point 
// to NULL
int *need_to_delete_ptr = int_memory_manager.release();
delete need_to_delete_ptr;
Finally, if you happen to want to reuse an auto_ptr object, you can simply call the reset function to free the old memory and set the new memory:
std::auto_ptr<int> int_memory_manager(new int);
int_memory_manager.reset(new int);
Using auto_ptr won't solve all of your problems. First, you could still write code that manually handles pointers that are also managed by auto_ptr objects. Doing this could result in double deletes. Whenever you request manual control of a pointer from an auto_ptr, you open yourself up to the requirement to call delete on that pointer -- consequently, the possibility of a memory leak exists.

Summary

The auto_ptr class is a templated class that takes a type and stores a pointer of that type, which can be set by the constructor or by using the reset function to pass in a new address to store. Pointers can be returned to manual control by the release function; otherwise, the pointer will be freed when the auto_ptr leaves scope.

The Good
  • auto_ptr objects store pointers and handle deleting the pointer when the auto_ptr object goes out of scope
  • Using auto_ptr helps avoid memory leaks associated with exceptions and minimizes the amount of cleanup code required
The Gotchas
  • Copying an auto_ptr changes the object being copied by setting the contents to NULL
  • auto_ptr objects are not guaranteed to work correctly with the standard template library containers

Related articles

Pointers and memory allocation in C++

Templated classes in C++