Exceptions and Pointers

Memory management of dynamically allocated object is always an issue. We need to make sure that alll allocated objects get destroyed to avoid a memory leak. When exceptions enter the picture, this becomes more of a challenge.

It's typical for a function to aquire a resource (e.g. dynamic memory), do some processing, then release the resource as in this simple function.

void my1stFunction( ) { // dynamically allocate a Fred object Fred *pFred = new Fred; // call some functions for Fred // more code here // destroy the Fred object delete pFred; } This code is all well and good until we learn that some member functions of Fred throw exceptions. The problem is that when an exception is thrown and not caught by my1stFunction, a memory leak occurs because the statement delete pFred; is never executed. One (not-so-elegant) solution is to delete the Fred object in a catch block. void myNew1stFunction( ) { // dynamically allocate a Fred object Fred *pFred = new Fred; try { // call some function of Fred } catch( ... ) // for all exceptions { delete pFred; // delete the object throw; // rethrow to higher level } // destroy the Fred object // if no exception and normal termination delete pFred; } This code works, but we've duplicated some code and if more objects are involved, more try/catch blocks are needed. The code becomes complicated, redundant and error prone. The solution is to use an auto_ptr

auto_ptrs

An auto_ptr is a kind of "smart pointer" provided by the C++ library. A "smart pointer" takes ownership of the dynamically allocated memory to which it points and frees the memory whenever the smart pointer itself is destroyed.

The auto_ptr type is just one such "smart pointer". When an auto_ptr gets destroyed, the object to which it points also gets destroyed (by auto_ptr's destructor), eliminating any chance of a memory leak.

void myBest1stFunction ( ) { // dynamically allocate a Fred object // note the syntax auto_ptr<Fred> p(new Fred); // call some Fred functions // do some stuff // no need for 'delete' because when 'p' // goes out of scope, its destructor will // automatically delete the Fred object } Note that the try/catch blocks and the delete statement are no longer required in order to delete the memory. No matter how the function ends, the auto_ptr will go out scope (it's a local variable) and be destroyed, taking the Fred object with it.

Using auto_ptrs

One of the fundamental issues when pointers to dynamically allocated memory is used is that of "ownership". By "ownership", we mean the function/object/piece of code that is responsible for deleting the memory to which the pointer points. We saw this earlier this semester when we discussed copy constructors and assignment operators for classes and the need for "deep copy" and "cloning". Smart pointers like auto_ptr can be used in any code, not just code that needs to handle exceptions.

Using auto_ptr is just as easy as using "regular" pointer. Doing so properly assures that a dynamically allocated object is owned by just one pointer and no memory leak or seg fault will occur. Only one auto_ptr may own an object, but ownership may be easily passed from one auto_ptr to another or to/from a regular pointer.

The auto_ptr (and all smart pointers) supports pointer dereferencing (using *) and using of the arrow ( -> ), just like "regular" pointers, but does not allow pointer arithmetic. To use auto_ptr you must #include <memory>.

#include <memory> void my2ndFunction( ) { Fred* p1 = new Fred; // p1 "owns" the Fred object auto_ptr<Fred> p2( p1 ); // pass ownership to an auto_ptr // use the auto_ptr the same way // we'd use a regular pointer *p2 = someOtherFred; // same as "*p1 = someOtherFred;" p2->SomeFredFunction(); // same as "p1->SomeFredFunction();" // use release() to take back ownership Fred* p3 = p2.release(); // now p3 "owns" the Fred object delete p3; // need to manually delete the Fred // p2 doesn't own any Fred object, and so won't try // to delete one } We can also have multiple auto_ptrs in the same code.
auto_ptrs support assignment which transfers ownership. void my3rdFunction() { auto_ptr<Fred> p1( new Fred ); auto_ptr<Fred> p2; p1->SomeFredFunction( ); // OK p2 = p1; // p2 owns the Fred object and p1 does not p2->SomeFredFunction( ); // OK // watch for this pitfall p1->SomeFredFunction( ); // error! p1 is a null pointer // when p1 and p2 go out of scope // p2's destructor deletes the Fred object // p1's destructor does nothing }

This is a short introduction to auto_ptr. There are several caveats and restrictions of which you should be aware before attempting to integrate them into your code. Check out your favorite C++ STL reference book for details.


Last Modified: Monday, 28-Aug-2006 10:16:05 EDT