Back to Item 27: Requiring or prohibiting heap-based objects
Continue to Item 29: Reference counting

Item 28: Smart pointers.

Smart pointers are objects that are designed to look, act, and feel like built-in pointers, but to offer greater functionality. They have a variety of applications, including resource management (see Items 9, 10, 25, and 31) and the automation of repetitive coding tasks (see Items 17 and 29).

When you use smart pointers in place of C++'s built-in pointers (i.e., dumb pointers), you gain control over the following aspects of pointer behavior:

- Construction and destruction.
You determine what happens when a smart pointer is created and destroyed. It is common to give smart pointers a default value of 0 to avoid the headaches associated with uninitialized pointers. Some smart pointers are made responsible for deleting the object they point to when the last smart pointer pointing to the object is destroyed. This can go a long way toward eliminating resource leaks.
- Copying and assignment.
You control what happens when a smart pointer is copied or is involved in an assignment. For some smart pointer types, the desired behavior is to automatically copy or make an assignment to what is pointed to, i.e., to perform a deep copy. For others, only the pointer itself should be copied or assigned. For still others, these operations should not be allowed at all. Regardless of what behavior you consider "right," the use of smart pointers lets you call the shots.
- Dereferencing.
What should happen when a client refers to the object pointed to by a smart pointer? You get to decide. You could, for example, use smart pointers to help implement the lazy fetching strategy outlined in Item 17.

Smart pointers are generated from templates because, like built-in pointers, they must be strongly typed; the template parameter specifies the type of object pointed to. Most smart pointer templates look something like this:

    template<class T>                    // template for smart
    class SmartPtr {                     // pointer objects
    public:
      SmartPtr(T* realPtr = 0);          // create a smart ptr to an
                                         // obj given a dumb ptr to
                                         // it; uninitialized ptrs
                                         // default to 0 (null)

      SmartPtr(const SmartPtr& rhs);     // copy a smart ptr

      ~SmartPtr();                       // destroy a smart ptr

      // make an assignment to a smart ptr
      SmartPtr& operator=(const SmartPtr& rhs);

      T* operator->() const;             // dereference a smart ptr
                                         // to get at a member of
                                         // what it points to

      T& operator*() const;              // dereference a smart ptr

    private:
      T *pointee;                        // what the smart ptr
    };                                   // points to

The copy constructor and assignment operator are both shown public here. For smart pointer classes where copying and assignment are not allowed, they would typically be declared private (see Item E27). The two dereferencing operators are declared `const`, because dereferencing a pointer doesn't modify it (though it may lead to modification of what the pointer points to). Finally, each smart pointer-to-`T` object is implemented by containing a dumb pointer-to-`T` within it. It is this dumb pointer that does the actual pointing.

Before going into the details of smart pointer implementation, it's worth seeing how clients might use smart pointers. Consider a distributed system in which some objects are local and some are remote. Access to local objects is generally simpler and faster than access to remote objects, because remote access may require remote procedure calls or some other way of communicating with a distant machine.

For clients writing application code, the need to handle local and remote objects differently is a nuisance. It is more convenient to have all objects appear to be located in the same place. Smart pointers allow a library to offer this illusion:

    template<class T>                    // template for smart ptrs
    class DBPtr {                        // to objects in a
    public:                              // distributed DB

      DBPtr(T *realPtr = 0);             // create a smart ptr to a
                                         // DB object given a local
                                         // dumb pointer to it

      DBPtr(DataBaseID id);              // create a smart ptr to a
                                         // DB object given its
                                         // unique DB identifier

      ...                                // other smart ptr
    };                                   // functions as above

    class Tuple {                        // class for database
    public:                              // tuples
      ...
      void displayEditDialog();          // present a graphical
                                         // dialog box allowing a
                                         // user to edit the tuple

      bool isValid() const;              // return whether *this
    };                                   // passes validity check

    // class template for making log entries whenever a T
    // object is modified; see below for details
    template<class T>
    class LogEntry {
    public:
      LogEntry(const T& objectToBeModified);
      ~LogEntry();
    };

    void editTuple(DBPtr<Tuple>& pt)
    {
      LogEntry<Tuple> entry(*pt);        // make log entry for this
                                         // editing operation; see
                                         // below for details

      // repeatedly display edit dialog until valid values
      // are provided
      do {
        pt->displayEditDialog();
      } while (pt->isValid() == false);
    }

The tuple to be edited inside `editTuple` may be physically located on a remote machine, but the programmer writing `editTuple` need not be concerned with such matters; the smart pointer class hides that aspect of the system. As far as the programmer is concerned, all tuples are accessed through objects that, except for how they're declared, act just like run-of-the-mill built-in pointers.

Notice the use of a `LogEntry` object in `editTuple`. A more conventional design would have been to surround the call to `displayEditDialog` with calls to begin and end the log entry. In the approach shown here, the `LogEntry`'s constructor begins the log entry and its destructor ends the log entry. As Item 9 explains, using an object to begin and end logging is more robust in the face of exceptions than explicitly calling functions, so you should accustom yourself to using classes like `LogEntry`. Besides, it's easier to create a single `LogEntry` object than to add separate calls to start and stop an entry.

As you can see, using a smart pointer isn't much different from using the dumb pointer it replaces. That's testimony to the effectiveness of encapsulation. Clients of smart pointers are supposed to be able to treat them as dumb pointers. As we shall see, sometimes the substitution is more transparent than others.

Construction, Assignment, and Destruction of Smart Pointers

Construction of a smart pointer is usually straightforward: locate an object to point to (typically by using the smart pointer's constructor arguments), then make the smart pointer's internal dumb pointer point there. If no object can be located, set the internal pointer to 0 or signal an error (possibly by throwing an exception).

Implementing a smart pointer's copy constructor, assignment operator(s) and destructor is complicated somewhat by the issue of ownership. If a smart pointer owns the object it points to, it is responsible for deleting that object when it (the smart pointer) is destroyed. This assumes the object pointed to by the smart pointer is dynamically allocated. Such an assumption is common when working with smart pointers. (For ideas on how to make sure the assumption is true, see Item 27.)

Consider the `auto_ptr` template from the standard C++ library. As Item 9 explains, an `auto_ptr` object is a smart pointer that points to a heap-based object until it (the `auto_ptr`) is destroyed. When that happens, the `auto_ptr`'s destructor deletes the pointed-to object. The `auto_ptr` template might be implemented like this:

    template<class T>
    class auto_ptr {
    public:
      auto_ptr(T *ptr = 0): pointee(ptr) {}
      ~auto_ptr() { delete pointee; }
      ...

    private:
      T *pointee;
    };

This works fine provided only one `auto_ptr` owns an object. But what should happen when an `auto_ptr` is copied or assigned?

    auto_ptr<TreeNode> ptn1(new TreeNode);

    auto_ptr<TreeNode> ptn2 = ptn1;      // call to copy ctor;
                                         // what should happen?

    auto_ptr<TreeNode> ptn3;

    ptn3 = ptn2;                         // call to operator=;
                                         // what should happen?

If we just copied the internal dumb pointer, we'd end up with two `auto_ptr`s pointing to the same object. This would lead to grief, because each `auto_ptr` would delete what it pointed to when the `auto_ptr` was destroyed. That would mean we'd delete an object more than once. The results of such double-deletes are undefined (and are frequently disastrous).

An alternative would be to create a new copy of what was pointed to by calling `new`. That would guarantee we didn't have too many `auto_ptr`s pointing to a single object, but it might engender an unacceptable performance hit for the creation (and later destruction) of the new object. Furthermore, we wouldn't necessarily know what type of object to create, because an `auto_ptr<T>` object need not point to an object of type `T`; it might point to an object of a type derived from `T`. Virtual constructors (see Item 25) can help solve this problem, but it seems inappropriate to require their use in a general-purpose class like `auto_ptr`.

The problems would vanish if `auto_ptr` prohibited copying and assignment, but a more flexible solution was adopted for the `auto_ptr` classes: object ownership is transferred when an `auto_ptr` is copied or assigned:

    template<class T>
    class auto_ptr {
    public:
      ...

      auto_ptr(auto_ptr<T>& rhs);        // copy constructor

      auto_ptr<T>&                       // assignment
      operator=(auto_ptr<T>& rhs);       // operator

      ...
    };

    template<class T>
    auto_ptr<T>::auto_ptr(auto_ptr<T>& rhs)
    {
      pointee = rhs.pointee;             // transfer ownership of
                                         // *pointee to *this

      rhs.pointee = 0;                   // rhs no longer owns
    }                                    // anything

    template<class T>
    auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs)
    {
      if (this == &rhs)                  // do nothing if this
        return *this;                    // object is being assigned
                                         // to itself

      delete pointee;                    // delete currently owned
                                         // object

      pointee = rhs.pointee;             // transfer ownership of
      rhs.pointee = 0;                   // *pointee from rhs to *this

      return *this;
    }

Notice that the assignment operator must delete the object it owns before assuming ownership of a new object. If it failed to do this, the object would never be deleted. Remember, nobody but the `auto_ptr` object owns the object the `auto_ptr` points to.

Because object ownership is transferred when `auto_ptr`'s copy constructor is called, passing `auto_ptr`s by value is often a very bad idea. Here's why:

    // this function will often lead to disaster
    void printTreeNode(ostream& s, auto_ptr<TreeNode> p)
    { s << *p; }

    int main()
    {
      auto_ptr<TreeNode> ptn(new TreeNode);

      ...

      printTreeNode(cout, ptn);          // pass auto_ptr by value

      ...

    }

When `printTreeNode`'s parameter `p` is initialized (by calling `auto_ptr`'s copy constructor), ownership of the object pointed to by `ptn` is transferred to `p`. When `printTreeNode` finishes executing, `p` goes out of scope and its destructor deletes what it points to (which is what `ptn` used to point to). `ptn`, however, no longer points to anything (its underlying dumb pointer is null), so just about any attempt to use it after the call to `printTreeNode` will yield undefined behavior. Passing `auto_ptr`s by value, then, is something to be done only if you're sure you want to transfer ownership of an object to a (transient) function parameter. Only rarely will you want to do this.

This doesn't mean you can't pass `auto_ptr`s as parameters, it just means that pass-by-value is not the way to do it. Pass-by-reference-to-`const` is:

    // this function behaves much more intuitively
    void printTreeNode(ostream& s,
                       const auto_ptr<TreeNode>& p)
    { s << *p; }

In this function, `p` is a reference, not an object, so no constructor is called to initialize `p`. When `ptn` is passed to this version of `printTreeNode`, it retains ownership of the object it points to, and `ptn` can safely be used after the call to `printTreeNode`. Thus, passing `auto_ptr`s by reference-to-`const` avoids the hazards arising from pass-by-value. (For other reasons to prefer pass-by-reference to pass-by-value, check out Item E22.)

The notion of transferring ownership from one smart pointer to another during copying and assignment is interesting, but you may have been at least as interested in the unconventional declarations of the copy constructor and assignment operator. These functions normally take `const` parameters, but above they do not. In fact, the code above changes these parameters during the copy or the assignment. In other words, `auto_ptr` objects are modified if they are copied or are the source of an assignment!

Yes, that's exactly what's happening. Isn't it nice that C++ is flexible enough to let you do this? If the language required that copy constructors and assignment operators take `const` parameters, you'd probably have to cast away the parameters' `const`ness (see Item E21) or play other games to implement ownership transferral. Instead, you get to say exactly what you want to say: when an object is copied or is the source of an assignment, that object is changed. This may not seem intuitive, but it's simple, direct, and, in this case, accurate.

If you find this examination of `auto_ptr` member functions interesting, you may wish to see a complete implementation. You'll find one on pages 291-294, where you'll also see that the `auto_ptr` template in the standard C++ library has copy constructors and assignment operators that are more flexible than those described here. In the standard `auto_ptr` template, those functions are member function templates, not just member functions. (Member function templates are described later in this Item. You can also read about them in Item E25.)

A smart pointer's destructor often looks like this:

    template<class T>
    SmartPtr<T>::~SmartPtr()
    {
      if (*this owns *pointee) {
        delete pointee;
      }
    }

Sometimes there is no need for the test. An `auto_ptr` always owns what it points to, for example. At other times the test is a bit more complicated. A smart pointer that employs reference counting (see Item 29) must adjust a reference count before determining whether it has the right to delete what it points to. Of course, some smart pointers are like dumb pointers: they have no effect on the object they point to when they themselves are destroyed.

Implementing the Dereferencing Operators

Let us now turn our attention to the very heart of smart pointers, the `operator*` and `operator->` functions. The former returns the object pointed to. Conceptually, this is simple:

    template<class T>
    T& SmartPtr<T>::operator*() const
    {
      perform "smart pointer" processing;

      return *pointee;
    }

First the function does whatever processing is needed to initialize or otherwise make `pointee` valid. For example, if lazy fetching is being used (see Item 17), the function may have to conjure up a new object for `pointee` to point to. Once `pointee` is valid, the `operator*` function just returns a reference to the pointed-to object.

Note that the return type is a reference. It would be disastrous to return an object instead, though compilers will let you do it. Bear in mind that `pointee` need not point to an object of type `T`; it may point to an object of a class derived from `T`. If that is the case and your `operator*` function returns a `T` object instead of a reference to the actual derived class object, your function will return an object of the wrong type! (This is the slicing problem. See Item E22 and Item 13.) Virtual functions invoked on the object returned from your star-crossed `operator*` will not invoke the function corresponding to the dynamic type of the pointed-to object. In essence, your smart pointer will not properly support virtual functions, and how smart is a pointer like that? Besides, returning a reference is more efficient anyway, because there is no need to construct a temporary object (see Item 19). This is one of those happy occasions when correctness and efficiency go hand in hand.

If you're the kind who likes to worry, you may wonder what you should do if somebody invokes `operator*` on a null smart pointer, i.e., one whose embedded dumb pointer is null. Relax. You can do anything you want. The result of dereferencing a null pointer is undefined, so there is no "wrong" behavior. Wanna throw an exception? Go ahead, throw it. Wanna call `abort` (possibly by having an `assert` call fail)? Fine, call it. Wanna walk through memory setting every byte to your birth date modulo 256? That's okay, too. It's not nice, but as far as the language is concerned, you are completely unfettered.

The story with `operator->` is similar to that for `operator*`, but before examining `operator->`, let us remind ourselves of the unusual meaning of a call to this function. Consider again the `editTuple` function that uses a smart pointer-to-`Tuple` object:

    void editTuple(DBPtr<Tuple>& pt)
    {
      LogEntry<Tuple> entry(*pt);

      do {
        pt->displayEditDialog();
      } while (pt->isValid() == false);
    }

The statement

    pt->displayEditDialog();

is interpreted by compilers as:

    (pt.operator->())->displayEditDialog();

That means that whatever `operator->` returns, it must be legal to apply the member-selection operator (`->`) to it. There are thus only two things `operator->` can return: a dumb pointer to an object or another smart pointer object. Most of the time, you'll want to return an ordinary dumb pointer. In those cases, you implement `operator->` as follows:

    template<class T>
    T* SmartPtr<T>::operator->() const
    {
      perform "smart pointer" processing;

      return pointee;
    }

This will work fine. Because this function returns a pointer, virtual function calls via `operator->` will behave the way they're supposed to.

For many applications, this is all you need to know about smart pointers. The reference-counting code of Item 29, for example, draws on no more functionality than we've discussed here. If you want to push your smart pointers further, however, you must know more about dumb pointer behavior and how smart pointers can and cannot emulate it. If your motto is "Most people stop at the Z — but not me!", the material that follows is for you.

Testing Smart Pointers for Nullness

With the functions we have discussed so far, we can create, destroy, copy, assign, and dereference smart pointers. One of the things we cannot do, however, is find out if a smart pointer is null:

    SmartPtr<TreeNode> ptn;

    ...

    if (ptn == 0) ...                    // error!

    if (ptn) ...                         // error!

    if (!ptn) ...                        // error!

This is a serious limitation.

It would be easy to add an `isNull` member function to our smart pointer classes, but that wouldn't address the problem that smart pointers don't act like dumb pointers when testing for nullness. A different approach is to provide an implicit conversion operator that allows the tests above to compile. The conversion traditionally employed for this purpose is to `void*`:

    template<class T>
    class SmartPtr {
    public:
      ...
      operator void*();                  // returns 0 if the smart
      ...                                // ptr is null, nonzero
    };                                   // otherwise

    SmartPtr<TreeNode> ptn;

    ...

    if (ptn == 0) ...                    // now fine

    if (ptn) ...                         // also fine

    if (!ptn) ...                        // fine

This is similar to a conversion provided by the iostream classes, and it explains why it's possible to write code like this:

    ifstream inputFile("datafile.dat");

    if (inputFile) ...                   // test to see if inputFile
                                         // was successfully
                                         // opened

Like all type conversion functions, this one has the drawback of letting function calls succeed that most programmers would expect to fail (see Item 5). In particular, it allows comparisons of smart pointers of completely different types:

    SmartPtr<Apple> pa;
    SmartPtr<Orange> po;

    ...

    if (pa == po) ...                    // this compiles!

Even if there is no `operator==` taking a `SmartPtr<Apple>` and a `SmartPtr<Orange>`, this compiles, because both smart pointers can be implicitly converted into `void*` pointers, and there is a built-in comparison function for built-in pointers. This kind of behavior makes implicit conversion functions dangerous. (Again, see Item 5, and keep seeing it over and over until you can see it in the dark.)

There are variations on the conversion-to-`void*` motif. Some designers advocate conversion to `const` `void*`, others embrace conversion to `bool`. Neither of these variations eliminates the problem of allowing mixed-type comparisons.

There is a middle ground that allows you to offer a reasonable syntactic form for testing for nullness while minimizing the chances of accidentally comparing smart pointers of different types. It is to overload `operator!` for your smart pointer classes so that `operator!` returns `true` if and only if the smart pointer on which it's invoked is null:

    template<class T>
    class SmartPtr {
    public:
      ...
      bool operator!() const;            // returns true if and only
      ...                                // if the smart ptr is null

    };

This lets your clients program like this,

    SmartPtr<TreeNode> ptn;

    ...

    if (!ptn) {                          // fine
      ...                                // ptn is null
    }
    else {
      ...                                // ptn is not null
    }

but not like this:

    if (ptn == 0) ...                    // still an error

    if (ptn) ...                         // also an error

The only risk for mixed-type comparisons is statements such as these:

    SmartPtr<Apple> pa;
    SmartPtr<Orange> po;

    ...

    if (!pa == !po) ...                 // alas, this compiles

Fortunately, programmers don't write code like this very often. Interestingly, iostream library implementations provide an `operator!` in addition to the implicit conversion to `void*`, but these two functions typically test for slightly different stream states. (In the C++ library standard (see Item E49 and Item 35), the implicit conversion to `void*` has been replaced by an implicit conversion to `bool`, and `operator` `bool` always returns the negation of `operator!`.)

Converting Smart Pointers to Dumb Pointers

Sometimes you'd like to add smart pointers to an application or library that already uses dumb pointers. For example, your distributed database system may not originally have been distributed, so you may have some old library functions that aren't designed to use smart pointers:

    class Tuple { ... };                 // as before

    void normalize(Tuple *pt);           // put *pt into canonical
                                         // form; note use of dumb
                                         // pointer

Consider what will happen if you try to call `normalize` with a smart pointer-to-`Tuple`:

    DBPtr<Tuple> pt;

    ...

    normalize(pt);                       // error!

The call will fail to compile, because there is no way to convert a `DBPtr<Tuple>` to a `Tuple*`. You can make it work by doing this,

    normalize(&*pt);                     // gross, but legal

but I hope you'll agree this is repugnant.

The call can be made to succeed by adding to the smart pointer-to-T template an implicit conversion operator to a dumb pointer-to-T:

    template<class T>                   // as before
    class DBPtr {
    public:
      ...
      operator T*() { return pointee; }
      ...
    };

    DBPtr<Tuple> pt;

    ...

    normalize(pt);                       // this now works

Addition of this function also eliminates the problem of testing for nullness:

    if (pt == 0) ...                     // fine, converts pt to a
                                         // Tuple*

    if (pt) ...                          // ditto

    if (!pt) ...                         // ditto (reprise)

However, there is a dark side to such conversion functions. (There almost always is. Have you been seeing Item 5?) They make it easy for clients to program directly with dumb pointers, thus bypassing the smarts your pointer-like objects are designed to provide:

    void processTuple(DBPtr<Tuple>& pt)
    {
      Tuple *rawTuplePtr = pt;           // converts DBPtr<Tuple> to
                                         // Tuple*

      use rawTuplePtr to modify the tuple;

    }

Usually, the "smart" behavior provided by a smart pointer is an essential component of your design, so allowing clients to use dumb pointers typically leads to disaster. For example, if `DBPtr` implements the reference-counting strategy of Item 29, allowing clients to manipulate dumb pointers directly will almost certainly lead to bookkeeping errors that corrupt the reference-counting data structures.

Even if you provide an implicit conversion operator to go from a smart pointer to the dumb pointer it's built on, your smart pointer will never be truly interchangeable with the dumb pointer. That's because the conversion from a smart pointer to a dumb pointer is a user-defined conversion, and compilers are forbidden from applying more than one such conversion at a time. For example, suppose you have a class representing all the clients who have accessed a particular tuple:

    class TupleAccessors {
    public:
      TupleAccessors(const Tuple *pt);   // pt identifies the
      ...                                // tuple whose accessors
    };                                   // we care about

As usual, `TupleAccessors`' single-argument constructor also acts as a type-conversion operator from `Tuple*` to `TupleAccessors` (see Item 5). Now consider a function for merging the information in two `TupleAccessors` objects:

    TupleAccessors merge(const TupleAccessors& ta1,
                         const TupleAccessors& ta2);

Because a `Tuple*` may be implicitly converted to a `TupleAccessors`, calling `merge` with two dumb `Tuple*` pointers is fine:

    Tuple *pt1, *pt2;

    ...

    merge(pt1, pt2);                     // fine, both pointers are converted
                                         // to TupleAccessors objects

The corresponding call with smart `DBPtr<Tuple>` pointers, however, fails to compile:

    DBPtr<Tuple> pt1, pt2;

    ...

    merge(pt1, pt2);                     // error! No way to convert pt1 and
                                         // pt2 to TupleAccessors objects

That's because a conversion from `DBPtr<Tuple>` to `TupleAccessors` calls for two user-defined conversions (one from `DBPtr<Tuple>` to `Tuple*` and one from `Tuple*` to `TupleAccessors`), and such sequences of conversions are prohibited by the language.

Smart pointer classes that provide an implicit conversion to a dumb pointer open the door to a particularly nasty bug. Consider this code:

    DBPtr<Tuple> pt = new Tuple;

    ...

    delete pt;

This should not compile. After all, `pt` is not a pointer, it's an object, and you can't delete an object. Only pointers can be deleted, right?

Right. But remember from Item 5 that compilers use implicit type conversions to make function calls succeed whenever they can, and recall from Item 8 that use of the `delete` operator leads to calls to a destructor and to `operator` `delete`, both of which are functions. Compilers want these function calls to succeed, so in the `delete` statement above, they implicitly convert `pt` to a `Tuple*`, then they delete that. This will almost certainly break your program.

If `pt` owns the object it points to, that object is now deleted twice, once at the point where `delete` is called, a second time when `pt`'s destructor is invoked. If `pt` doesn't own the object, somebody else does. That somebody may be the person who deleted `pt`, in which case all is well. If, however, the owner of the object pointed to by `pt` is not the person who deleted `pt`, we can expect the rightful owner to delete that object again later. The first and last of these scenarios leads to an object being deleted twice, and deleting an object more than once yields undefined behavior.

This bug is especially pernicious because the whole idea behind smart pointers is to make them look and feel as much like dumb pointers as possible. The closer you get to this ideal, the more likely your clients are to forget they are using smart pointers. If they do, who can blame them if they continue to think that in order to avoid resource leaks, they must call `delete` if they called `new`?

The bottom line is simple: don't provide implicit conversion operators to dumb pointers unless there is a compelling reason to do so.

Smart Pointers and Inheritance-Based Type Conversions

Suppose we have a public inheritance hierarchy modeling consumer products for storing music:

    class MusicProduct {
    public:
      MusicProduct(const string& title);
      virtual void play() const = 0;
      virtual void displayTitle() const = 0;
      ...
    };

    class Cassette: public MusicProduct {
    public:
      Cassette(const string& title);
      virtual void play() const;
      virtual void displayTitle() const;
      ...
    };

    class CD: public MusicProduct {
    public:
      CD(const string& title);
      virtual void play() const;
      virtual void displayTitle() const;
      ...
    };

Further suppose we have a function that, given a `MusicProduct` object, displays the title of the product and then plays it:

    void displayAndPlay(const MusicProduct* pmp, int numTimes)
    {
      for (int i = 1; i <= numTimes; ++i) {
        pmp->displayTitle();
        pmp->play();
      }
    }

Such a function might be used like this:

    Cassette *funMusic = new Cassette("Alapalooza");
    CD *nightmareMusic = new CD("Disco Hits of the 70s");

    displayAndPlay(funMusic, 10);
    displayAndPlay(nightmareMusic, 0);

There are no surprises here, but look what happens if we replace the dumb pointers with their allegedly smart counterparts:

    void displayAndPlay(const SmartPtr<MusicProduct>& pmp,
                        int numTimes);

    SmartPtr<Cassette> funMusic(new Cassette("Alapalooza"));
    SmartPtr<CD> nightmareMusic(new CD("Disco Hits of the 70s"));

    displayAndPlay(funMusic, 10);         // error!
    displayAndPlay(nightmareMusic, 0);    // error!

If smart pointers are so brainy, why won't these compile?

They won't compile because there is no conversion from a `SmartPtr<CD>` or a `SmartPtr<Cassette>` to a `SmartPtr<MusicProduct>`. As far as compilers are concerned, these are three separate classes — they have no relationship to one another. Why should compilers think otherwise? After all, it's not like `SmartPtr<CD>` or `SmartPtr<Cassette>` inherits from`SmartPtr<MusicProduct>`. With no inheritance relationship between these classes, we can hardly expect compilers to run around converting objects of one type to objects of other types.

Fortunately, there is a way to get around this limitation, and the idea (if not the practice) is simple: give each smart pointer class an implicit type conversion operator (see Item 5) for each smart pointer class to which it should be implicitly convertible. For example, in the music hierarchy, you'd add an `operator` `SmartPtr<MusicProduct>` to the smart pointer classes for `Cassette` and `CD`:

    class SmartPtr<Cassette> {
    public:
      operator SmartPtr<MusicProduct>()
      { return SmartPtr<MusicProduct>(pointee); }

      ...

    private:
      Cassette *pointee;
    };

    class SmartPtr<CD> {
    public:
      operator SmartPtr<MusicProduct>()
      { return SmartPtr<MusicProduct>(pointee); }

      ...

    private:
      CD *pointee;
    };

The drawbacks to this approach are twofold. First, you must manually specialize the `SmartPtr` class instantiations so you can add the necessary implicit type conversion operators, but that pretty much defeats the purpose of templates. Second, you may have to add many such conversion operators, because your pointed-to object may be deep in an inheritance hierarchy, and you must provide a conversion operator for each base class from which that object directly or indirectly inherits. (If you think you can get around this by providing only an implicit type conversion operator for each direct base class, think again. Because compilers are prohibited from employing more than one user-defined type conversion function at a time, they can't convert a smart pointer-to-`T` to a smart pointer-to-indirect-base-class-of-`T` unless they can do it in a single step.)

It would be quite the time-saver if you could somehow get compilers to write all these implicit type conversion functions for you. Thanks to a recent language extension, you can. The extension in question is the ability to declare (nonvirtual) member function templates (usually just called member templates), and you use it to generate smart pointer conversion functions like this:

    template<class T>                    // template class for smart
    class SmartPtr {                     // pointers-to-T objects
    public:
      SmartPtr(T* realPtr = 0);

      T* operator->() const;
      T& operator*() const;

      template<class newType>             // template function for
      operator SmartPtr<newType>()        // implicit conversion ops.
      {
        return SmartPtr<newType>(pointee);
      }

      ...
    };

Now hold on to your headlights, this isn't magic — but it's close. It works as follows. (I'll give a specific example in a moment, so don't despair if the remainder of this paragraph reads like so much gobbledygook. After you've seen the example, it'll make more sense, I promise.) Suppose a compiler has a smart pointer-to-`T` object, and it's faced with the need to convert that object into a smart pointer-to-base-class-of-`T`. The compiler checks the class definition for `SmartPtr<T>` to see if the requisite conversion operator is declared, but it is not. (It can't be: no conversion operators are declared in the template above.) The compiler then checks to see if there's a member function template it can instantiate that would let it perform the conversion it's looking for. It finds such a template (the one taking the formal type parameter `newType`), so it instantiates the template with `newType` bound to the base class of `T` that's the target of the conversion. At that point, the only question is whether the code for the instantiated member function will compile. In order for it to compile, it must be legal to pass the (dumb) pointer `pointee` to the constructor for the smart pointer-to-base-of-`T`. `pointee` is of type `T`, so it is certainly legal to convert it into a pointer to its (public or protected) base classes. Hence, the code for the type conversion operator will compile, and the implicit conversion from smart pointer-to-`T` to smart pointer-to-base-of-`T` will succeed.

An example will help. Let us return to the music hierarchy of CDs, cassettes, and music products. We saw earlier that the following code wouldn't compile, because there was no way for compilers to convert the smart pointers to CDs or cassettes into smart pointers to music products:

    void displayAndPlay(const SmartPtr<MusicProduct>& pmp,
                        int howMany);

    SmartPtr<Cassette> funMusic(new Cassette("Alapalooza"));
    SmartPtr<CD> nightmareMusic(new CD("Disco Hits of the 70s"));

    displayAndPlay(funMusic, 10);         // used to be an error
    displayAndPlay(nightmareMusic, 0);    // used to be an error

With the revised smart pointer class containing the member function template for implicit type conversion operators, this code will succeed. To see why, look at this call:

    displayAndPlay(funMusic, 10);

The object `funMusic` is of type `SmartPtr<Cassette>`. The function `displayAndPlay` expects a `SmartPtr<MusicProduct>` object. Compilers detect the type mismatch and seek a way to convert `funMusic` into a `SmartPtr<MusicProduct>` object. They look for a single-argument constructor (see Item 5) in the `SmartPtr<MusicProduct>` class that takes a `SmartPtr<Cassette>`, but they find none. They look for an implicit type conversion operator in the `SmartPtr<Cassette>` class that yields a `SmartPtr<MusicProduct>` class, but that search also fails. They then look for a member function template they can instantiate to yield one of these functions. They discover that the template inside `SmartPtr<Cassette>`, when instantiated with `newType` bound to `MusicProduct`, generates the necessary function. They instantiate the function, yielding the following code:

    SmartPtr<Cassette>::  operator SmartPtr<MusicProduct>()
    {
      return SmartPtr<MusicProduct>(pointee);
    }

Will this compile? For all intents and purposes, nothing is happening here except the calling of the `SmartPtr<MusicProduct>` constructor with `pointee` as its argument, so the real question is whether one can construct a `SmartPtr<MusicProduct>` object with a `Cassette*` pointer. The `SmartPtr<MusicProduct>` constructor expects a `MusicProduct*` pointer, but now we're on the familiar ground of conversions between dumb pointer types, and it's clear that `Cassette*` can be passed in where a `MusicProduct*` is expected. The construction of the `SmartPtr<MusicProduct>` is therefore successful, and the conversion of the `SmartPtr<Cassette>` to `SmartPtr<MusicProduct>` is equally successful. Voilà! Implicit conversion of smart pointer types. What could be simpler?

Furthermore, what could be more powerful? Don't be misled by this example into assuming that this works only for pointer conversions up an inheritance hierarchy. The method shown succeeds for any legal implicit conversion between pointer types. If you've got a dumb pointer type `T1*` and another dumb pointer type `T2*`, you can implicitly convert a smart pointer-to-`T1` to a smart pointer-to-`T2` if and only if you can implicitly convert a `T1*` to a `T2*`.

This technique gives you exactly the behavior you want — almost. Suppose we augment our `MusicProduct` hierarchy with a new class, `CasSingle`, for representing cassette singles. The revised hierarchy looks like this:

Now consider this code:

    template<class T>                    // as above, including member tem-
    class SmartPtr { ... };              // plate for conversion operators

    void displayAndPlay(const SmartPtr<MusicProduct>& pmp,
                        int howMany);

    void displayAndPlay(const SmartPtr<Cassette>& pc,
                        int howMany);

    SmartPtr<CasSingle> dumbMusic(new CasSingle("Achy Breaky Heart"));

    displayAndPlay(dumbMusic, 1);        // error!

In this example, `displayAndPlay` is overloaded, with one function taking a `SmartPtr<MusicProduct>` object and the other taking a `SmartPtr<Cassette>` object. When we invoke `displayAndPlay` with a `SmartPtr<CasSingle>`, we expect the `SmartPtr<Cassette>` function to be chosen, because `CasSingle` inherits directly from `Cassette` and only indirectly from `MusicProduct`. Certainly that's how it would work with dumb pointers. Alas, our smart pointers aren't that smart. They employ member functions as conversion operators, and as far as C++ compilers are concerned, all calls to conversion functions are equally good. As a result, the call to `displayAndPlay` is ambiguous, because the conversion from `SmartPtr<CasSingle>` to `SmartPtr<Cassette>` is no better than the conversion to `SmartPtr<MusicProduct>`.

Implementing smart pointer conversions through member templates has two additional drawbacks. First, support for member templates is rare, so this technique is currently anything but portable. In the future, that will change, but nobody knows just how far in the future that will be. Second, the mechanics of why this works are far from transparent, relying as they do on a detailed understanding of argument-matching rules for function calls, implicit type conversion functions, implicit instantiation of template functions, and the existence of member function templates. Pity the poor programmer who has never seen this trick before and is then asked to maintain or enhance code that relies on it. The technique is clever, that's for sure, but too much cleverness can be a dangerous thing.

Let's stop beating around the bush. What we really want to know is how we can make smart pointer classes behave just like dumb pointers for purposes of inheritance-based type conversions. The answer is simple: we can't. As Daniel Edelson has noted, smart pointers are smart, but they're not pointers. The best we can do is to use member templates to generate conversion functions, then use casts (see Item 2) in those cases where ambiguity results. This isn't a perfect state of affairs, but it's pretty good, and having to cast away ambiguity in a few cases is a small price to pay for the sophisticated functionality smart pointers can provide.

Smart Pointers and const

Recall that for dumb pointers, `const` can refer to the thing pointed to, to the pointer itself, or both (see Item E21):

    CD goodCD("Flood");

    const CD *p;                         // p is a non-const pointer
                                         // to a const CD object

    CD * const p = &goodCD;              // p is a const pointer to
                                         // a non-const CD object;
                                         // because p is const, it
                                         // must be initialized

    const CD * const p = &goodCD;        // p is a const pointer to
                                         // a const CD object

Naturally, we'd like to have the same flexibility with smart pointers. Unfortunately, there's only one place to put the `const`, and there it applies to the pointer, not to the object pointed to:

    const SmartPtr<CD> p =                   // p is a const smart ptr
      &goodCD;                               // to a non-const CD object

This seems simple enough to remedy — just create a smart pointer to a `const` `CD`:

    SmartPtr<const CD> p =                   // p is a non-const smart ptr
      &goodCD;                               // to a const CD object

Now we can create the four combinations of `const` and non-`const` objects and pointers we seek:

    SmartPtr<CD> p;                          // non-const object,
                                             // non-const pointer

    SmartPtr<const CD> p;                    // const object,
                                             // non-const pointer

    const SmartPtr<CD> p = &goodCD;          // non-const object,
                                             // const pointer

    const SmartPtr<const CD> p = &goodCD;    // const object,
                                             // const pointer

Alas, this ointment has a fly in it. Using dumb pointers, we can initialize `const` pointers with non-`const` pointers and we can initialize pointers to `const` objects with pointers to non-`const`s; the rules for assignments are analogous. For example:

    CD *pCD = new CD("Famous Movie Themes");

    const CD * pConstCD = pCD;               // fine

But look what happens if we try the same thing with smart pointers:

    SmartPtr<CD> pCD = new CD("Famous Movie Themes");

    SmartPtr<const CD> pConstCD = pCD;       // fine?

`SmartPtr<CD>` and `SmartPtr<const` `CD>` are completely different types. As far as your compilers know, they are unrelated, so they have no reason to believe they are assignment-compatible. In what must be an old story by now, the only way these two types will be considered assignment-compatible is if you've provided a function to convert objects of type `SmartPtr<CD>` to objects of type `SmartPtr<const` `CD>`. If you've got a compiler that supports member templates, you can use the technique shown above for automatically generating the implicit type conversion operators you need. (I remarked earlier that the technique worked anytime the corresponding conversion for dumb pointers would work, and I wasn't kidding. Conversions involving `const` are no exception.) If you don't have such a compiler, you have to jump through one more hoop.

Conversions involving `const` are a one-way street: it's safe to go from non-`const` to `const`, but it's not safe to go from `const` to non-`const`. Furthermore, anything you can do with a `const` pointer you can do with a non-`const` pointer, but with non-`const` pointers you can do other things, too (for example, assignment). Similarly, anything you can do with a pointer-to-`const` is legal for a pointer-to-non-`const`, but you can do some things (such as assignment) with pointers-to-non-`const`s that you can't do with pointers-to-`const`s.

These rules sound like the rules for public inheritance (see Item E35). You can convert from a derived class object to a base class object, but not vice versa, and you can do anything to a derived class object you can do to a base class object, but you can typically do additional things to a derived class object, as well. We can take advantage of this similarity when implementing smart pointers by having each smart pointer-to-`T` class publicly inherit from a corresponding smart pointer-to-`const`-`T` class:

    template<class T>                    // smart pointers to const
    class SmartPtrToConst {              // objects

      ...                                // the usual smart pointer
                                         // member functions

    protected:
      union {
        const T* constPointee;           // for SmartPtrToConst access
        T* pointee;                      // for SmartPtr access
      };
    };

    template<class T>                    // smart pointers to
    class SmartPtr:                      // non-const objects
      public SmartPtrToConst<T> {
      ...                                // no data members
    };

With this design, the smart pointer-to-non-`const`-`T` object needs to contain a dumb pointer-to-non-`const`-`T`, and the smart pointer-to-`const`-`T` needs to contain a dumb pointer-to-`const`-`T`. The naive way to handle this would be to put a dumb pointer-to-`const`-`T` in the base class and a dumb pointer-to-non-`const`-`T` in the derived class. That would be wasteful, however, because `SmartPtr` objects would contain two dumb pointers: the one they inherited from `SmartPtrToConst` and the one in `SmartPtr` itself.

This problem is resolved by employing that old battle axe of the C world, a union, which can be as useful in C++ as it is in C. The union is protected, so both classes have access to it, and it contains both of the necessary dumb pointer types. `SmartPtrToConst<T>` objects use the `constPointee` pointer, `SmartPtr<T>` objects use the `pointee` pointer. We therefore get the advantages of two different pointers without having to allocate space for more than one. (See Item E10 for another example of this.) Such is the beauty of a union. Of course, the member functions of the two classes must constrain themselves to using only the appropriate pointer, and you'll get no help from compilers in enforcing that constraint. Such is the risk of a union.

With this new design, we get the behavior we want:

    SmartPtr<CD> pCD = new CD("Famous Movie Themes");

    SmartPtrToConst<CD> pConstCD = pCD;     // fine

Evaluation

That wraps up the subject of smart pointers, but before we leave the topic, we should ask this question: are they worth the trouble, especially if your compilers lack support for member function templates?

Often they are. The reference-counting code of Item 29, for example, is greatly simplified by using smart pointers. Furthermore, as that example demonstrates, some uses of smart pointers are sufficiently limited in scope that things like testing for nullness, conversion to dumb pointers, inheritance-based conversions, and support for pointers-to-`const`s are irrelevant. At the same time, smart pointers can be tricky to implement, understand, and maintain. Debugging code using smart pointers is more difficult than debugging code using dumb pointers. Try as you may, you will never succeed in designing a general-purpose smart pointer that can seamlessly replace its dumb pointer counterpart.

Smart pointers nevertheless make it possible to achieve effects in your code that would otherwise be difficult to implement. Smart pointers should be used judiciously, but every C++ programmer will find them useful at one time or another.

Back to Item 27: Requiring or prohibiting heap-based objects
Continue to Item 29: Reference counting