Topic : Smart pointer templates in C++
Author : David Harvey
Page : 1 Next >>
Go to page :


Smart pointer templates in C++
David Harvey


1995/1996 David Harvey. All rights reserved
Email: david-AT-davethehat-DOT-com
Home: http:www.davethehat.com
Revised 27 June 97, 20 October 97, 30 November 97
Acknowledgements to Object Designers Limited for permission to make this paper available here.
This article offers an explanation of a useful C++ template idiom, which is then put to work in an implementation of typed object variables.  




Although templates have been part of C++ for some time, the last year or so has seen a surge of interest in the potential of this feature of the language. This is partly due to the increased availability of reliable (well, reasonably reliable) implementations in popular compilers, and partly to the influence on the ANSI/ISO C++ draft standard of Stepanov and Lee's Standard Template Library. Because of the extensive use of templates in the proposed standard library for C++, we are all going to become much more familiar with using them. The library's idioms will spawn imitation and enhancement, and I predict that in a couple of years we will be spending more time talking templates than inheritance in the C++ community.

Currently, the most familiar use of template classes in C++ is to provide parameterised, type-safe collections. This idiom has clear advantages over the cumbersome inheritance-based or pointer-to-void collection classes provided by compiler vendors whose tools do not support templates. The purpose of this article, however, is to explore another template idiom, providing a class which allows us to wrap an instance of another class, maintaining access to that class's members, but adding functionality in a way that is transparent to the wrapped class. By overloading the dereferencing operator -> for the template class, we can use instances of specialisations of the template class as pointers to the contained class, while keeping control of access, lifetime and ownership. We'll look first at building a simple reference counting mechanism, before continuing to an implementation of typed object reference variables. These manage references and lifetimes of their objects, automatically deleting them when they are no longer referenced.

The key to this idiom is the overload of the dereferencing operator. By wrapping an instance of a class in a template class, and returning the address of the wrapped instance from the dereferencing operator, we can still access any of the wrapped class's members:

template <class T>
class Wrapper {
public:
        T* operator->() { return &myT; }
private:
        T myT;
};

int main() {
        Wrapper<Thing> wThing;
        wThing->Foo();  // calls Thing::Foo()
        ...
}


C++ defines special treatment for the dereferencing operator. When applied to an object it must be followed by a member (variable or function) name. The operand is effectively replaced in the expression by its result. If the operator returns an object or reference which also supplies an overloaded dereferencing operator, this too is applied, and so on until a pointer is returned. The name of the member is then used against this to call the requested member function or access the required member data. If the final class which results from this process has no member of the given name, the compiler displays an error message.
As it stands, this example doesn't add anything to our class: it simply forwards calls to its contained instance. Before going on, consider what assumptions are being made for the template parameter class T. Clearly, any class wrapped by this template must have a default constructor. If it doesn't, the compiler will complain when we try to instantiate the template with the class. This would appear to restrict us somewhat, as it is not always the case that an interesting class has a default constructor. We certainly don't want to provide our template class with a constructor for each possible parameter class.

There are two ways around the problem. Firstly we could define a constructor for the template class which takes as an argument a const reference to an instance of the parameter class. Here is our new template class:

template <class T>
class Wrapper1 {
public:
        Wrapper1(const T& rT) : myT(rT) {}
        T* operator->()                 { return &myT; }
private:
        T myT;
};

int main() {
        Wrapper1<Thing> wThing(Thing(10,20));
        ...
}

This makes it possible to initialise the contained instance with a temporary object created just for the purposes of this initialisation. This works, but is inefficient: we have created two instances of the parameter class, not just one. Moreover, this idiom in turn relies on the parameter class having a copy constructor, either generated by the compiler or defined by the class implementer. A class may have chosen to prevent copying of its instances by declaring the copy constructor private: such a class cannot be wrapped by this template. On the plus side, we have the advantage of being able to initialise the template class from an already-existing instance of the wrapped class. It doesn't have to be a temporary object created for the purpose.
A second alternative is to store a pointer to the wrapped class in the template class, rather than a whole instance:

template <class T>
class Wrapper2 {
public:
        Wrapper2(T* pT) : my_pT(pT) {}
        T* operator->()             { return my_pT; }
private:
        T* my_pT;
};

int main() {
        Thing* pThing = new Thing(10,20);
        Wrapper2<Thing> wThing(pThing);
        ...
        delete pThing;
        // DANGER: wThing now invalid!
}

Now only one instance of the wrapped class is created. We preserve the advantage from the previous version of being able to wrap an existing instance, and can use the template class to wrap a class whose copy constructor is not accessible. But now we need to manage the pointer which we pass to the template constructor. If all we want is a wrapper for a single instance in a defined lifetime, having to delete the instance separately is cumbersome and potentially error-prone. We might try to create the new instance as a temporary pointer in the constructor call for the template class, and have the template delete the pointer itself in its destructor:
template <class T>
class Wrapper3 {
public:
        Wrapper3(T* pT) : my_pT(pT) {}
        ~Wrapper3()                 { delete my_pT; }
        T* operator->()             { return my_pT; }
private:
        T* my_pT;
};

int main() {
        Wrapper3<Thing> wThing(new Thing(10,20));
        ...
}

This means that the ownership of the pointer is held by the template class instance, so if we pass a pointer to an already-existing instance, we have to be sure not to delete it. Moreover, we had better not pass in the address of a stack or static instance of the wrapped class:
...
Thing danger(5,10)
Wrapper3<Thing> wThing(&danger);
..
When wThing goes out of scope and its destructor is called, an attempt will be made to delete memory held on the stack. This is always bad news, as adjacent data on the stack will be corrupted. To use this solution requires some care and discipline, but we will see that it can be a useful tactic.
Often, we need to manage objects on the basis of the number of references to an instance. We can use this template idiom to provide a wrapper for any class which we want to be able to use in this way. To keep things simple, the counted class provides facilities for manual increment and decrement of references: we will look at a way of automating this later. Here is the class: for now, we assume that a default constructor is available for parameter classes.

template <class T>
class Counted {
public:
        Counted() : Count(0) {}
        ~Counted()           { ASSERT(Count == 0); }

        unsigned GetRef()    { return ++Count; }
        unsigned FreeRef()   { ASSERT(Count > 0);


Page : 1 Next >>