Friday, July 4, 2008

Auto Pointers

Here is a cute thing - auto pointers. Basically they are supposed to solve the problem of doing a new and forgetting to delete the resulting memory.

This can happen more easily than you may think if a function throws an exception.

Below is an example of using them. Be warned that the MS VC library and the gcc library seem to handle auto_ptr a bit differently. The MS STL library simply transfers ownership of the pointer (so you can use it after assigning it) whilst the gcc STL library actually sets the original pointer to NULL (thus, you can't use it after assigning it). We should be very careful about assigning auto pointers because of this.

Also, I note from the STL code that the destructor deletes the pointer, it doesn't allow for an array. What I mean is that if you do this:


int * i = new int [100];


You are supposed to delete it like this:


delete [] i;


Since the auto_ptr doesn't know to do that, this will not work properly:


auto_ptr i (new int [100]);


However I should point out that if you are using STL in the first place, you should probably be using a vector instead of an array of ints in the first place. ;)

Note also you don't delete an auto_ptr - you simply let it delete itself in the destructor when it goes out of scope.



#include
#include
#include
#include

using namespace std;

class Widget
{
public:
Widget () { cout << "widget constructor" << endl; };
~Widget () { cout << "widget destructor" << endl; };
void test () { cout << "widget test" << endl; }
};

// output operator for auto_ptr (see Josuttis p 47)

template
ostream& operator<< (ostream& strm, const auto_ptr& p)
{
if (p.get () == NULL)
strm << "NULL";
else
strm << p.get ();
return strm;
} // end of ostream& operator<<

void sub (void)
{
auto_ptr w (new Widget);
auto_ptr x; // NULL pointer at this time

w->test (); // test it (use like a normal pointer)

cout << "Before assignment..." << endl;
cout << " w: " << w << endl;
cout << " x: " << x << endl;

// transfer ownership of pointer from w to x
x = w;

x->test (); // test it again

cout << "After assignment..." << endl;
cout << " w: " << w << endl;
cout << " x: " << x << endl;

throw runtime_error ("Trouble brewing");

}

int main (void)
{

try
{
cout << "Before sub" << endl;
sub ();
cout << "After sub" << endl;
}
catch (runtime_error & e)
{
cout << "Exception: " << e.what () << endl;
}

return 0;
} // end of main




Output on Linux

Before sub
widget constructor
widget test
Before assignment...
w: 0x804a880
x: NULL
widget test
After assignment...
w: NULL
x: 0x804a880
widget destructor
Exception: Trouble brewing



Output on MS VC++

Before sub
widget constructor
widget test
Before assignment...
w: 00300030
x: NULL
widget test
After assignment...
w: 00300030
x: 00300030
widget destructor
Exception: Trouble brewing


Notice the difference, after the assignment the pointer still exists (is not NULL) in this case.