mario::konrad
programming / C++ / sailing / nerd stuff
Auto Pointer
© 2004 / Mario Konrad

Introduction

Every C++ programmer knows the auto_ptr from the standard library, which is a nice thing, by the way. The only problem this template has, it cannot be used within containers from the standard library. As soon as you like to put pointers into a container, you are on your own to clean them up afterwards (and take care of the objects).

As a standard behaviour, I find this totally ok, but sometimes an automatic cleanup of dynamic created objects at the time of destruction of a container would be handy.

This is the topic this page is all about: an auto_ptr feature but able to use them within a container. Although this page is about auto_ptr, the basiscs about the usage of this featue is not covered.

Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#ifndef __A_PTR_H__
#define __A_PTR_H__

template <class T> class a_ptr
{
    private:
        T * ptr;
    public:
        explicit a_ptr(T * ptr = 0) throw()
            : ptr(ptr) {}

        a_ptr(const a_ptr & p) throw()
        {
            ptr = const_cast<a_ptr<T>*>(&p)->release();
        }

        ~a_ptr() throw()
        {
            if (ptr) delete ptr;
            ptr = 0;
        }

        inline a_ptr & operator=(const a_ptr & p) throw()
        {
            ptr = const_cast<a_ptr<T>*>(&p)->release();
            return *this;
        }

        inline T & operator*(void) throw() { return *ptr; }
        inline T * operator->(void) throw() { return ptr; }
        inline T * get(void) throw() { return ptr; }

        inline T * release(void) throw()
        {
            T * p = ptr; ptr = 0; return p;
        }

        inline a_ptr & reset(T * p = 0) throw()
        {
            if (p != ptr)
            {
                delete ptr;
                ptr = p;
            }
            return *this;
        }

        template <class S> a_ptr & operator=(const a_ptr<S> & p) throw()
        {
            ptr = dynamic_cast<T*>(const_cast<a_ptr<S>*>(&p)->release());
            return *this;
        }

        template <class S> a_ptr(const a_ptr<S> & p) throw()
        {
            ptr = dynamic_cast<T*>(const_cast<a_ptr<S>*>(&p)->release());
        }
};

#endif

Demo

For demonstration purpose we need the following class Item:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Item
{
  private:
    static int gid; // global id
    static int act; // number of active objects
    static int con; // number of constructor calls
    static int des; // number of destructor calls
    int id; // actual id of the object
  public:
    Item() : id(gid++) { ++act; ++con; }
    Item(const Item & i) : id(i.id) { ++act; ++con; }
    ~Item() { --act; ++des; }
    static void clear() { act = con = des = 0; }
    static void show(ostream &);
    friend ostream & operator<<(ostream &, const Item &);
};

int Item::gid = 0;
int Item::act = 0;
int Item::con = 0;
int Item::des = 0;

void Item::show(ostream & os)
{
  os `<< "con<" << con << ">` des`<" << des << ">` act`<" << act << ">`";
}

ostream & operator<<(ostream & os, const Item & i)
{
  return os `<< "<" << i.id << ">`";
}

With this class, we're able to perform a simple test:

1
2
3
4
void test_1(void)
{
  a_ptr`<Item>` ptr(new Item);
}

The function test_1 creates an object from the class Item and puts it into the automatic pointer ptr. The object does not get destroyed at the end of the function, instead the automatic pointer takes care of it and destroys the object. This is auto_ptr standard behaviour.

Far more interesting is the usage of the new automatic pointer with containters:

1
2
3
4
5
6
7
8
void test_2(void)
{
  vector`<a_ptr<Item>` > vec;
  vec.push_back(a_ptr`<Item>`(new Item));
  vec.push_back(a_ptr`<Item>`(new Item));
  vec.push_back(a_ptr`<Item>`(new Item));
  vec.push_back(a_ptr`<Item>`(new Item));
}

Try this with std::auto_ptr, and you are lost ;-)

Another nice example is to access those objects within the container. To make it a bit more interesing we have a dynamic created container, handled as well in an automatic pointer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void test_3(void)
{
  typedef a_ptr`<Item>` Item_ptr;
  typedef vector`<Item_ptr>` Item_vec;

  a_ptr`<Item_vec>` vec(new Item_vec);
  vec->push_back(Item_ptr(new Item));
  vec->push_back(Item_ptr(new Item));
  vec->push_back(Item_ptr(new Item));
  vec->push_back(Item_ptr(new Item));
  for (Item_vec::iterator i = vec->begin(); i != vec->end(); ++i)
    cout << "  " << **i;
  cout << endl;
}

For the last Demonstration, we'll need another class ItemFoo, derived from class Item:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ItemFoo : public Item
{
  private:
    int dummy;
  public:
    ItemFoo() : dummy(-1) {}
    ~ItemFoo() {}
    friend ostream & operator<<(ostream &, const ItemFoo &);
};

ostream & operator<<(ostream & os, const ItemFoo & i)
{
  return os `<< "foo<" << i.dummy << ">`";
}

The demo shows that polymorphism works, even if you use the a_ptr (remember ItemFoo is derived from Item):

1
2
3
4
5
6
7
void test_4(void)
{
  a_ptr`<ItemFoo>` f(new ItemFoo);
  a_ptr`<Item>` b;
  // ...
  b = f;
}

Download

All source files provided by this page are free to modify, distribute or to use them in any kind you like. Use them on your own risk.