4.3. AutoPtr Class

AutoPtr is based upon SGI implementation of a auto_ptr<> template that makes dynamic memory handling a little bit easier. This class predates times when C++ standard library did not provide auto_ptr class of its own.

AutoPtr interface does not completely confirm to that of auto_ptr<> as specified by the C++ Standard.

4.3.1. DEFINITION


template <class X> class AutoPtr 
{
public:
    explicit AutoPtr(X* p_ = 0);
    AutoPtr (AutoPtr& a_);
    ~AutoPtr();

    AutoPtr& operator= (AutoPtr& a_); 

    X* get() const  { return m_ptr; }
    X* release() { m_owns = false; return m_ptr; }
};
	  

4.3.2. USAGE

Usage is straight forward:


#include <assa/AutoPtr.h>

class A;

void foo()
{
    AutoPtr<A> a (new A());

    // ...
}
	  

When creating a new AutoPtr<T> object, use the constructor syntax of AutoPtr<T> ap(p).

When variable a goes out of scope, AutoPtr<>'s destructor deallocates the memory for you. AutoPtr<> thus guards dynamic memory for the programmer and takes care of it.

We can optionally ask the AutoPtr object to relinquish its responsibility over a chunk of memory we have entrusted to it. This can be used for holding onto something we want to return in normal case, but deallocate in exceptional situation. Here is one such situation:


void foo (); // may throw something

// return newly allocated value on success,
// or 0 pointer on failure.

int* ptr ()
{
    try {
        AutoPtr<int>  i_ptr (new int (i));
        foo ();
        return i_ptr.release ();
    }
    catch (...) {
        return 0;
    }
}
	  

Above, an exception thrown from foo() results in the destruction of the object i_ptr before the call of the release() function, which means that the object pointed to by i_ptr is going to be deleted.

If, however, foo() does not throw an exception, i_ptr.release () is called and the value of i_ptr is returned to the caller of function ptr().

Both assignment operator and copy constructor are not really what names stand for - they both are ownership transfer operations. They modify the left hand side of an assignment by giving up their ownership of the memory they guard.


AutoPtr<int>  p1 (new int (31)); // p1 owns the memory
AutoPtr<int>  p2;                // p2 doesn't own anything
    
p2 = p1;   // now p2 owns the memory area, p1 doesn't
           // p1 becomes the 0 'pointer'

AutoPtr<int>  p3 (p2);  // now p3 owns the memory, not p2
	  

AutoPtr cannot accidentally be confused with normal pointers. This is done by explicitly prohibiting all implicit conversions between pointer types and AutoPtr types.


int* p;

AutoPtr<int>  ap = p; // Illegal: a pointer cannot be implicitly
                      // converted to an AutoPtr

ap = p; // Illegal too for the same reason
	  

Often you would need to declare AutoPtr instance in the outer scope, and then, later, based on conditional control flow, bind it to the right pointer (if any). For example:


AutoPtr<T> tp;

... some code later ...

if ( condition == true ) 
{
   AutoPtr<T> temp (new T);  // allocate memory
   tp = temp;                // reassign ownership
   ...
}
	  

Passing AutoPtr by value is often a very bad idea:

		
class A {};

void foo (AutoPtr<A> a_ )
{
    cout << a_->dump();
}

int main() 
{
    AutoPtr<A> pa ( new A );

    // ...
    foo ( pa );  // pass AutoPtr by value
    // ...
}
	  

When foo()'s parameter a_ is initialized by calling AutoPtr's constructor, ownership of the object pointed to by pa is transferred to a_. When foo() exits, a_ goes out of scope and its destructor deletes memory area of object A. pa now holds the dangling pointer and any attempt to use pa will most likely cause a core dump.

4.3.3. Examples

AutoPtr test program can be found in $src/tests/autoptr_test.cpp.