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.23] How do I provide reference counting with copy-on-write semantics?

Reference counting can be done with either pointer semantics or reference semantics. The previous FAQ shows how to do reference counting with pointer semantics. This FAQ shows how to do reference counting with reference semantics.

The basic idea is to allow users to think they're copying your Fred objects, but in reality the underlying implementation doesn't actually do any copying unless and until some user actually tries to modify the underlying Fred object.

Class Fred::Data houses all the data that would normally go into the Fred class. Fred::Data also has an extra data member, count_, to manage the reference counting. Class Fred ends up being a "smart reference" that (internally) points to a Fred::Data.

class Fred {
public:

  Fred();                               // A default constructor
  Fred(int i, int j);                   // A normal constructor

  Fred(Fred const& f);
  Fred& operator= (Fred const& f);
 ~Fred();

  void sampleInspectorMethod() const;   // No changes to this object
  void sampleMutatorMethod();           // Change this object

  ...

private:

  class Data {
  public:
    Data();
    Data(int i, int j);
    Data(Data const& d);

    // Since only Fred can access a Fred::Data object,
    // you can make Fred::Data's data public if you want.
    // But if that makes you uncomfortable, make the data private
    // and make Fred a friend class via friend class Fred;
    ...data goes here...

    unsigned count_;
    // count_ is the number of Fred objects that point at this
    // count_ must be initialized to 1 by all constructors
    // (it starts as 1 since it is pointed to by the Fred object that created it)
  };

  Data* data_;
};

Fred::Data::Data()              : count_(1) /*init other data*/ { }
Fred::Data::Data(int i, int j)  : count_(1) /*init other data*/ { }
Fred::Data::Data(Data const& d) : count_(1) /*init other data*/ { }

Fred::Fred()             : data_(new Data()) { }
Fred::Fred(int i, int j) : data_(new Data(i, j)) { }

Fred::Fred(Fred const& f)
  : data_(f.data_)
{
  ++data_->count_;
}

Fred& Fred::operator= (Fred const& f)
{
  // 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::Data contains Freds)
  Data* const old = data_;
  data_ = f.data_;
  ++data_->count_;
  if (--old->count_ == 0) delete old;
  return *this;
}

Fred::~Fred()
{
  if (--data_->count_ == 0) delete data_;
}

void Fred::sampleInspectorMethod() const
{
  // This method promises ("const") not to change anything in *data_
  // Other than that, any data access would simply use "data_->..."
}

void Fred::sampleMutatorMethod()
{
  // This method might need to change things in *data_
  // Thus it first checks if this is the only pointer to *data_
  if (data_->count_ > 1) {
    Data* d = new Data(*data_);    // Invoke Fred::Data's copy ctor
    --data_->count_;
    data_ = d;
  }
  assert(data_->count_ == 1);

  // Now the method proceeds to access "data_->..." as normal
}
If it is fairly common to call Fred's default constructor, you can avoid all those new calls by sharing a common Fred::Data object for all Freds that are constructed via Fred::Fred(). To avoid static initialization order problems, this shared Fred::Data object is created "on first use" inside a function. Here are the changes that would be made to the above code (note that the shared Fred::Data object's destructor is never invoked; if that is a problem, either hope you don't have any static initialization order problems, or drop back to the approach described above):
class Fred {
public:
  ...
private:
  ...
  static Data* defaultData();
};

Fred::Fred()
  : data_(defaultData())
{
  ++data_->count_;
}

Fred::Data* Fred::defaultData()
{
  static Data* p = NULL;
  if (p == NULL) {
    p = new Data();
    ++p->count_;    // Make sure it never goes to zero
  }
  return p;
}
Note: You can also provide reference counting for a hierarchy of classes if your Fred class would normally have been a base class.