Monday, July 25, 2005

auto_ptr and their usability with STL containers

Aaaahh... Smart pointers..aren't they one of the best things that has happened to C++. They are also sometimes called Managed pointers. I am not going to talk about them right from introduction, so if you are unaware what they are and what are their uses, then try figuring them out through the web-searches or @ codeguru... my fav place as far as C++ is concerned.

Ok..Ok..not ready to search for them...fine.. I will try giving a very brief description. Remember when you use pointers in C++ with dynamic memory management, it's such a headache to get them going and finish them off once you are done? If you did a new or new[], you need to have a corresponding delete or delete[], so that the allocated memory is freed up and the destructor for the new-ed object is called. Also, when you are done with the delete/delete[], freed-up the memory, you need to cautiously set the pointer to NULL, so that you won't try dereferencing it after the de-allocation assuming it points to a valid object. (Recall Dangling pointers. They could be disastrous if dereferenced!)

But just imagine that you could use pointers in the same way you used to do and forget about the headache of the deleting (and optionally NULLing it)? How would that be? Wouldn't that be cool and care-free? ...and that is what is made possible by these managed pointers/smart pointers. They are really smart and they exist in wide varieties.

For now, I would limit my post to just one of that category which is the standard auto_ptr (read auto-pointers). They are a part of C++. They own the dynamically allocated object and then perform the automatic cleanup whenever the object goes out of scope or is not needed. Now, if at some stage you want the manual ownership back they have this function member - release(), make a call to it and all the headache is back yours.

//Example showing a bad situation with naked pointers
void MyFunction()
        MyClass* ptr( new MyClass);
        /*.some more code.*/
        delete ptr;

In this scenario, just in case of some exception it's possible that somehow the delete ptr; did not happen...Disasterous..a memory leak! There's more - suppose, in the destructor of MyClass, you call for some resource release like close database connections, releasing file handles or release semaphores, locks etc. All resources are blocked and there's no way out. There could be many more aweful situations.

Now, lets use an auto_ptr and see how it makes life easy:

//Situation above tackled using auto_ptr
void MyFunction()
        auto_ptr<MyClass> ptr(new MyClass);
        /*.some more code.*/

There is no need to explicitly call a delete; as soon as ptr goes out of scope the destructor of MyClass is called and the object is freed. You should note that auto_ptr only handles allocations with new and not those done with new[] or malloc or any other memory allocation routine for that matter. For those, refer to the following posts - Handling dynamic array allocations, Custom deleters with smart pointers, Free New, Delete Malloc

You can even transfer the ownership of an auto_ptr to another auto_ptr, to another function or free/reset it to use it again.

Here's an example showing the transfer of ownership from one auto_ptr to another.

        //Example showing transfer of ownership.
        auto_ptr<MyClass> autoptr1(new MyClass);
        auto_ptr<MyClass> autoptr2;
        autoptr2 = autoptr1;
        //Now autoptr2 owns the pointer & autoptr1 doesn't
        autoptr1->MyFunction(); // ERROR !!!
        //autoptr1, now is a null pointer

However, one very interesting and important fact about auto_ptr's is that their copies are not equivalent as is the case with naked pointers.
  • Consider STL containers or some generic algorithms/functions like sort. There is no restriction by the standard on the vendors of the STL implementers that the containers won't make a copy of the members being added into it (and probably do nothing with it!). In fact, for example, the implementations of the vector class make a copy of the original object and store inside them. We know auto_ptr copies are not equivalent due to the strict ownership semantics. You might be safe in just having those auto_ptrs stored inside a vector but you get a few examples around this in the Gotw link below that show how the problem comes out in the open, for example, with say, a sort algorithm that holds a copy of an element as a pivot? There is nothing to guarantee or prevent that.

  • Another disastrous situation would be when you are initializing (setting/assigning) another auto_ptr object with an object held by the vector. You would think that the object inside the vector is okay and retains the same state but it will not. The ownership by this time would get transferred to the external object just initialized/assigned.

  • What if there is a member function that returns the contained vectors element is returned by value?

  • What if you make another copy of the vector? Or for that matter, create some other container out of its elements?

  • Use of member functions of vector like - constructor, assign, insert, resize - will they succeed? Will they definitely succeed?

It just gets too messy to handle. Sure some operations will work out fine but quite a few will not and you would need to keep worrying about 'em.

To help us out of this disaster, the C++ standards did some tweaking. The copy constructor and the copy assignment operator of auto_ptr take a reference to a non-const right hand side objects and the standard containers' insert() takes a reference to a const and hence auto_ptr cannot be used. It will just not allow you to create a copy of auto_ptr inside the vector (standard containers) members. That will cause you compilation errors on a compliant compiler. Nice solution..isnt it? But still, as a strict guideline..we should not use the auto_ptr wherever a copy of theirs is expected to be made.

For more information, visit these : Using auto_ptr effectively by Herb Sutter, Smart Pointers - What, Why and Which, Smart pointers



Anonymous said...

cool page boss... will definetlt be visiting this one regularly... as i m also working on C++ - Parul

abnegator said...

Parul - welcome to my blog. well, haven't been updating this frequently but sure will try to. :).