Publications
of Jon Jagger
jon@jaggersoft.com
Appeared in Overload 17, January 1997

auto_ptr || !auto_ptr

This article takes a sideways glimpse at the auto_ptr class from the standard C++ library. It does this by considering the forces that shaped auto_ptr, and implements a class that seems to resolve those forces sensibly. However, the resulting class turns out to be very different to auto_ptr. Why is this? In all honesty I have to say that I feel it's because auto_ptr has lost its way and turned into something of a dog's dinner (a mess!).

The basic thing to remember with auto_ptr is that it is supposed to improve the safety of handling dynamically allocated objects within block structured code. What does that mean? Let's look at an example:

file_spy * smiley = new file_spy();
...
smiley->espionage();
...
delete smiley;
The problem with this code is that espionage() may throw an exception. If it does then the flow of control will never reach the delete expression and we will have a memory leak. One answer to this problem is to create a class whose destructor deletes the allocated object [1]. For example:
// owned_ptr.hpp
namespace accu
{
    template<typename type>
    class owned_ptr
    {
    public:

        explicit owned_ptr(type * acquire);
        ...
        ~owned_ptr();

    public: // query

        type * operator->() const;
        type & operator*() const;
        type * get() const; 
        ...
    private: // state

        type * resource;

    };
}

#include "owned_ptr-template.hpp"
// owned_ptr-template.hpp
namespace accu
{
    template<typename type>
    owned_ptr<type>::owned_ptr(type * acquire)
      : resource(acquire)
    {
        // all done
    }   
    
    template<typename type>
    owned_ptr<type>::~owned_ptr()
    {
        delete resource;
    }
    ...
    template<typename type>
    type * 
    owned_ptr<type>::operator->() const
    {
        return get();
    }

    template<typename type>
    type &
    owned_ptr<type>::operator*() const
    {
        return *get();
    }
    ...
}
Now the example can be re-written as:
owned_ptr<file_spy> smiley(new file_spy());
...
smiley->espionage();
and if espionage() throws an exception, the owned_ptr destructor will delete the dynamic file_spy, thus plugging the memory leak. The interesting part is when you try to flesh out owned_ptr to make it "minimal but complete". The most obvious missing parts are the copy constructor and the assignment operator. The usual signatures for these are:
namespace accu
{
    template<typename type>
    class owned_ptr
    {
        ...
        owned_ptr(const owned_ptr & other);
        owned_ptr & operator=(const owned_ptr & rhs);
        ...
    };
}
The problem, in both of these signtures, is the presence of the const. Suppose you write the copy constructor like this:
namespace accu
{
    template<typename type>
    owned_ptr<type>::owned_ptr(const owned_ptr & other)
      : resource(other.resource)
    {
        // all done
    } 
    ...
}
This is a sure fire recipe for disaster. You'll end up with multiple owned_ptr objects pointing to the same dynamically created object. Every owned_ptr object will attempt to delete the same object in its destructor. For example:
void dangle(owned_ptr<snafu> fubar)
{
    ...
}

owned_ptr<snafu> oops(new snafu(42));
dangle(oops);
Here dangle will copy construct the fubar parameter from oops and when dangle returns it will call the destructor for the copy constructed fubar parameter, thus deleting the snafu pointed to by oops. When oops goes out of scope it too will have its destructor called and it too will try to delete the object already deleted by the dangle parameter. How can you solve this problem? One way is to keep a count of how many "owners" are pointing to the resource. This is reference counting. However, owned_ptr takes a different approach. Instead owned_ptr ensures that a dynamically allocated object is only ever pointed to by one owner. In other words, if you write this:
snafu * p = new snafu(42);
owned_ptr<snafu> alpha(p);
owned_ptr<snafu> beta(alpha);
Then owned_ptr has to ensure that either alpha or beta ends up owning p, but not both. Given this it might seem logical to implement the copy constructor like this:
namespace accu
{
    template<typename type>
    owned_ptr<type>::owned_ptr(const owned_ptr & other)
      : resource(other.resource)
    {
        other.resource = 0;
    }
}
The idea here is that other passes the resource on to the newly constructed object. This won't work. The problem is that other is declared const — so the compiler won't allow the line:
        ...
        other.resource = 0;
        ...
You might try and fix this by making the copy constructor and copy assignment operator take a non-const reference (exactly as auto_ptr does):
namespace accu
{
    template<typename type>
    owned_ptr<type>::owned_ptr(owned_ptr & other)
      : resource(other.resource)
    {
        other.resource = 0;
    }

    template<typename type>
    owned_ptr<type>
    owned_ptr<type>::operator=(owned_ptr & rhs)
    {
        delete resource;
        resource = rhs.resource;
        rhs.resource = 0;
        return *this;
    }
}
This solves the problem, but up pops another (an ominous sign). The apparently legal code:
owned_ptr<snafu> f();         // function declaration
...
owned_ptr<snafu> delta(f());  // copy construction
now fails to compile. It fails because the copy constructor argument is an unnamed temporary object returned by f() and the C++ standard states that temporary objects cannot be bound to non-const reference parameters [2]. Okay, I hear you say, that's easy to work around, make the parameter a named non-temporary object. Like this:
owned_ptr<snafu> temp;
temp = f();
owned_ptr<snafu> delta(temp);
This won't work either. The reason is that the assignment is really just syntactic sugar for this:
temp.operator=(f());
and, once again, the parameter is an unnamed temporary. So, giving a non-const reference to the copy constructor and copy assignment operator of owned_ptr has had a surprising result: it has made it impossible to return an owned_ptr object from a function by copy. At this point I hope you feel owned_ptr is losing its way. It seems to be lurching from one problem to another, and the hack-work-arounds are simply making matters worse. Time to take a step back and start thinking again. Remember that the original motivation was to improve the exception safety of dynamically objects in block structured code. In this light, returning an owned_ptr from a function by copy can be seen as an attempt to misuse owned_ptr. A reasonable approach is therefore to revoke the copy constructor and the copy assignment operator completely. One way to do this is to inherit the inability to copy [3]:
namespace accu
{
    class non_copyable
    {
    protected:
        non_copyable() {}
    private:
        non_copyable(const non_copyable &);
        non_copyable & operator=(const non_copyable &); 
    };
}
...
namespace accu
{
    template<typename type>
    class owned_ptr : public non_copyable
    {
        ...
    };
}
But this is perhaps too cute. I prefer this:
namespace accu
{
    template<typename type>
    class owned_ptr
    {
        ...
    private: // inappropriate
       
        owned_ptr(const owned_ptr &);
        owned_ptr & operator=(const owned_ptr &);
        ...	
    };
}
What else needs doing to owned_ptr? Well, it's reasonable to provide a default constructor (to mirror a raw pointer). It also seems reasonable to provide methods to explicitly control ownership.
// owned_ptr.hpp
namespace accu
{
    template<typename type>
    class owned_ptr 
    {
    public: // birth/death

        explicit owned_ptr(type * acquire = 0);
        ~owned_ptr();

    public: // query

        type * operator->() const;
        type & operator*() const;
        type * get() const; 
        ...
    public: // ownership

        void transfer(owned_ptr & acquire_from);
        void swap(owned_ptr & swap_with);
        void reset(type * new_resource = 0);
        void release();

    private: // inappropriate
       
        owned_ptr(const owned_ptr &);
        owned_ptr & operator=(const owned_ptr &);

    private: // state

        type * resource;

    };
}

#include "owned_ptr-template.hpp"
// owned_ptr-template.hpp
...
namespace accu // owned_ptr - birth/death
{
    template<typename type>
    owned_ptr<type>::owned_ptr(type * acquire)
      : resource(acquire)
    {
        // all done
    }   
    
    template<typename type>
    owned_ptr<type>::~owned_ptr()
    {
        delete resource;
    }
}

namespace accu // owned_ptr - query
{
    template<typename type>
    type * 
    owned_ptr<type>::operator->() const
    {
        return get();
    }

    template<typename type>
    type &
    owned_ptr<type>::operator*() const
    {
        return *get();
    }

    template<typename type>
    type * 
    owned_ptr<type>::get() const
    {
        if (resource == 0)
        {
            throw ...
        }
        return resource;
    }
}

namespace accu // owned_ptr - ownership
{
    template<typename type>
    void 
    owned_ptr<type>::transfer(owned_ptr & acquire_from)
    {
        reset(acquire_from.release());
    }

    template<typename type>
    void 
    owned_ptr<type>::swap(owned_ptr & rhs)
    {
        std::swap(resource, rhs.resource); 
    }

    template<typename type>
    void 
    owned_ptr<type>::reset(type * new_resource)
    {
        type * old_resource = resource;
        resource = new_resource;        
        delete old_resource;
    }

    template<typename type>
    type * 
    owned_ptr<type>::release()
    {
        type * released = resource;
        resource = 0;
        return released;
    }
}
Finally, lets see a small example of owned_ptr is action. Suppose we have a design where a publisher object is responsible for notifying all registered subscriber objects when a resource changes [4]. Further, suppose that the subscriber objects need only retain a copy of the most up to date resource:
class resource;

class subscriber
{
public:
    virtual void update(const resource & updated) = 0;
};
class example : public subscriber
{
public:
    ... 
private:
    virtual void update(const resource & updated);
    owned_ptr<resource> latest;
    ...
};
void example::update(const resource & updated)
{
    latest.reset(new resource(updated));
}

That's all for now.
Cheers
Jon Jagger
jon@jaggersoft.com

[1] The C++ Programming Language, 2nd edition. Bjarne Stroustrup. Addison Wesley, ISBN 0-201-53992-6. Chapter 9.4 Resource Acquisition, p308.

also

The Design and Evolution of C++. Bjarne Stroustrup. Addison Wesley. ISBN 0-201-54330-3. Chapter 16.5 Resource Management, p388

[2] Many C++ compilers do not implement this yet.

[3] You can inherit inability as well as ability. For example you can inherit the inability to distinguish certain colours. It's called colour blindness.

[4] Design Patterns. Erich Gamma et. al. Addison Wesley. ISBN 0-201-63361-2. Observer, p293.