C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 16:
16.1 Does delete p delete the pointer p, or the pointed-to-data *p?
16.2 Is it safe to delete the same pointer twice?
16.3 Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?
16.4 Benefits of new over malloc()?
16.5 Can I use realloc() on pointers allocated via new?
16.6 Checking for NULL after p = new Fred()?
16.7 How can I convince my (older) compiler to automatically check new to see if it returns NULL?
16.8 Checking for NULL before delete p?
16.9 What are the two steps that happen when I say delete p?
16.10 Does p = new Fred() leak memory if the ctor throws an exception?
16.11 How do I allocate / unallocate an array of things?
16.12 What if I forget the [] when deleteing an array allocated via new T[n]?
16.13 Can I drop the [] when deleteing an array of some built-in type (char, int, etc)?
16.14 After p = new Fred[n], how does the compiler know there are n objects to be destructed during delete[] p?
16.15 Is it legal (and moral) for a member function to say delete this?
16.16 How do I allocate multidimensional arrays using new?
16.17 How to simplify the Matrix code from the previous FAQ?
16.18 How to make the Matrix class generic?
16.19 What's another way to build a Matrix template?
16.20 Does C++ have arrays whose length can be specified at run-time?
16.21 Allocating all objects via new, not local/global/static?
16.22 How do I do simple reference counting?
16.23 How do I provide reference counting with copy-on-write semantics?
16.24 How do I provide reference counting with copy-on-write semantics for a hierarchy of classes?
16.25 Preventing people from subverting the reference counting mechanism?
16.26 Can I use a garbage collector in C++?
16.27 What are the two kinds of garbage collectors for C++?
16.28 Where can I get more info on garbage collectors for C++?
[16.22] How do I do simple reference counting?

If all you want is the ability to pass around a bunch of pointers to the same object, with the feature that the object will automagically get deleted when the last pointer to it disappears, you can use something like the following "smart pointer" class:

// Fred.h

class FredPtr;

class Fred {
public:
  Fred() : count_(0) /*...*/ { }  // All ctors set count_ to 0 !
  ...
private:
  friend class FredPtr;     // A friend class
  unsigned count_;
  // count_ must be initialized to 0 by all constructors
  // count_ is the number of FredPtr objects that point at this
};

class FredPtr {
public:
  Fred* operator-> () { return p_; }
  Fred& operator* ()  { return *p_; }
  FredPtr(Fred* p)    : p_(p) { ++p_->count_; }  // p must not be NULL
 ~FredPtr()           { if (--p_->count_ == 0) delete p_; }
  FredPtr(FredPtr const& p) : p_(p.p_) { ++p_->count_; }
  FredPtr& operator= (FredPtr const& p)
        { // DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
          // (This order properly handles self-assignment)
          // (This order also properly handles recursion, e.g., if a Fred contains FredPtrs)
          Fred* const old = p_;
          p_ = p.p_;
          ++p_->count_;
          if (--old->count_ == 0) delete old;
          return *this;
        }
private:
  Fred* p_;    // p_ is never NULL
};
Naturally you can use nested classes to rename FredPtr to Fred::Ptr.

Note that you can soften the "never NULL" rule above with a little more checking in the constructor, copy constructor, assignment operator, and destructor. If you do that, you might as well put a p_ != NULL check into the "*" and "->" operators (at least as an assert()). I would recommend against an operator Fred*() method, since that would let people accidentally get at the Fred*.

One of the implicit constraints on FredPtr is that it must only point to Fred objects which have been allocated via new. If you want to be really safe, you can enforce this constraint by making all of Fred's constructors private, and for each constructor have a public (static) create() method which allocates the Fred object via new and returns a FredPtr (not a Fred*). That way the only way anyone could create a Fred object would be to get a FredPtr ("Fred* p = new Fred()" would be replaced by "FredPtr p = Fred::create()"). Thus no one could accidentally subvert the reference counting mechanism.

For example, if Fred had a Fred::Fred() and a Fred::Fred(int i, int j), the changes to class Fred would be:

class Fred {
public:
  static FredPtr create();              // Defined below class FredPtr {...};
  static FredPtr create(int i, int j);  // Defined below class FredPtr {...};
  ...
private:
  Fred();
  Fred(int i, int j);
  ...
};

class FredPtr { /* ... */ };

inline FredPtr Fred::create()             { return new Fred(); }
inline FredPtr Fred::create(int i, int j) { return new Fred(i,j); }
The end result is that you now have a way to use simple reference counting to provide "pointer semantics" for a given object. Users of your Fred class explicitly use FredPtr objects, which act more or less like Fred* pointers. The benefit is that users can make as many copies of their FredPtr "smart pointer" objects, and the pointed-to Fred object will automagically get deleted when the last such FredPtr object vanishes.

If you'd rather give your users "reference semantics" rather than "pointer semantics," you can use reference counting to provide "copy on write".