Scott Meyers

More Effective C++ Item 29 Source Code

Last Updated October 8, 2003
by Scott Meyers

 /******************************************************************************
 *                                                                             *
 *        Code from Item 29 ("Reference Counting") of MORE EFFECTIVE C++       *
 *                                                                             *
 *                               Scott Meyers                                  *   
 *                                                                             *
 *            Copyright 1996 (c) Addison-Wesley Publishing Company             *
 *       You are free to use this code for non-commercial purposes only.       *
 *                                                                             *
 * This page contains the code for the classes and class templates making up   *
 * the Reference Counting Item of More Effective C++.  To use this code,       *
 * either copy this page and paste it into a C++ source file or save the       *
 * entire page as text into a C++ source file.  Don't save the HTML source     *
 * and expect that to compile :-)                                              *
 *                                                                             *
 * Each class or template in this file follows a block comment that shows the  *
 * corresponding pages in the book.  This page also contains a main function   *
 * that performs a VERY limited test of the code in this file.  You can        *
 * compile the code in this file as a stand-alone program, and you should get  *
 * this output:                                                                *
 *                                                                             *
 *     String with no changes.                                                 *
 *     String with    changes.                                                 *
 *     10                                                                      *
 *     -1                                                                      *
 *                                                                             *
 * The code here reflects all changes made to date in response to bug reports  *
 * from readers of the book.  (To see a complete list of known bugs in More    *
 * Effective C++, as well as whether they've been fixed yet, visit the         *
 * More Effective C++ Errata List.)  If you find any additional bugs, please   *
 * send them to me.                                                            *
 ******************************************************************************/
 #include <iostream>      // The iostream facilities are not used in the classes
                          // in this file, but they are used in the code that 
                          // tests the classes.
 
 #include <cstring>       // This includes the C string functions, e.g.,
                          // strlen, strcpy, etc.  They are used in the
                          // implementation of class String::StringValue.
 
 // The following is for compilers that don't support bool.  Uncomment these
 // lines if your compilers lack bool support.  For details on this emulation
 // of bool, see More Effective C++, pp. 3-4.
 // typedef int bool;
 // const bool false = 0;
 // const bool true = 1;
 
 /******************************************************************************
 *                       Class RCObject (from pp. 204-205)                     *
 ******************************************************************************/
 class RCObject {                       // base class for reference-
 public:                                // counted objects
   void addReference();
   void removeReference();
   void markUnshareable();
   bool isShareable() const;
   bool isShared() const;
 
 protected:
   RCObject();
   RCObject(const RCObject& rhs);
   RCObject& operator=(const RCObject& rhs);
   virtual ~RCObject() = 0;
 
 private:
   int refCount;
   bool shareable;
 };
 
 RCObject::RCObject()
 : refCount(0), shareable(true) {}
 
 RCObject::RCObject(const RCObject&)
 : refCount(0), shareable(true) {}
 
 RCObject& RCObject::operator=(const RCObject&)
 {
   return *this;
 }  
 
 RCObject::~RCObject() {}
 
 void RCObject::addReference() 
 {
   ++refCount;
 }
 
 void RCObject::removeReference()
 {
   if (--refCount == 0) delete this;
 }
 
 void RCObject::markUnshareable()
 {
   shareable = false;
 }
 
 bool RCObject::isShareable() const
 {
   return shareable;
 }
 
 bool RCObject::isShared() const
 {
   return refCount > 1;
 }  
 
 
 /******************************************************************************
 *                 Template Class RCPtr (from pp. 203, 206)                    *
 ******************************************************************************/
 template<class T>                      // template class for smart
 class RCPtr {                          // pointers-to-T objects; T
 public:                                // must support the RCObject interface
   RCPtr(T* realPtr = 0);
   RCPtr(const RCPtr& rhs);
   ~RCPtr();
   RCPtr& operator=(const RCPtr& rhs);                     
   T* operator->() const;
   T& operator*() const;
 
 private:
   T *pointee;
   void init();
 };
 
 template<class T>
 void RCPtr<T>::init()
 {
   if (pointee == 0) return;
   
   if (pointee->isShareable() == false) {
     pointee = new T(*pointee);
   }
   
   pointee->addReference();
 }
 
 template<class T>
 RCPtr<T>::RCPtr(T* realPtr)
 : pointee(realPtr)
 {
   init();
 }
 
 template<class T>
 RCPtr<T>::RCPtr(const RCPtr& rhs)
 : pointee(rhs.pointee)
 { 
   init();
 }
 
 template<class T>
 RCPtr<T>::~RCPtr()
 {
   if (pointee) pointee->removeReference();
 }
 
 template<class T>
 RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs)
 {
   if (pointee != rhs.pointee) {                   // this code was modified
     T *oldPointee = pointee;                      // for the book's 10th
                                                   // printing
     pointee = rhs.pointee;
     init(); 
 
     if (oldPointee) oldPointee->removeReference();                
   }
   
   return *this;
 }
 
 template<class T>
 T* RCPtr<T>::operator->() const 
 {
   return pointee;
 }
 
 template<class T>
 T& RCPtr<T>::operator*() const
 {
   return *pointee;
 }
 
 
 /******************************************************************************
 *           Classes String and StringValue (from pp. 204, 206-207)            *
 ******************************************************************************/
 class String {                          // class to be used by
 public:                                 // application developers
   String(const char *value = "");
 
   char operator[](int index) const;       
   char& operator[](int index);
 
 private:
   // class representing string values
   struct StringValue: public RCObject {
     char *data;
  
     StringValue(const char *initValue);
     StringValue(const StringValue& rhs);
     void init(const char *initValue);
     ~StringValue();
   };
 
   RCPtr<StringValue> value;                       
 
   // This function is not in the book, but it's convenient for testing the
   // class -- see below.
   friend std::ostream& operator<<(std::ostream& stream, const String& string);
 };
 
 void String::StringValue::init(const char *initValue)
 {
   using namespace std;
 
   data = new char[strlen(initValue) + 1];
   strcpy(data, initValue);
 }
 
 String::StringValue::StringValue(const char *initValue)
 { 
   init(initValue);
 }
 
 String::StringValue::StringValue(const StringValue& rhs)
 {
   init(rhs.data);
 }
 
 String::StringValue::~StringValue()
 {
   delete [] data;
 }
 
 
 String::String(const char *initValue)
 : value(new StringValue(initValue)) {}
 
 char String::operator[](int index) const
 {
   return value->data[index];
 }
 
 char& String::operator[](int index)
 { 
   if (value->isShared()) {      
     value = new StringValue(value->data);
   }
 
   value->markUnshareable();
 
   return value->data[index];
 }
 
 std::ostream& operator<<(std::ostream& stream, const String& string)
 {
   return stream << string.value->data;
 }
 
 
 /******************************************************************************
 *                  Template Class RCIPtr (from pp. 209-210)                   *
 *                                                                             *
 * The code for RCIPtr has changed over the years in response to errors        *
 * both in the original source code as well as in the subsequent fixes.  You   *
 * can find a complete list of changes at the More Effective C++ errata page.  *
 * The code here is accurate as of the 13th printing of the book.              *
 ******************************************************************************/
 template<class T>
 class RCIPtr {
 public:
   RCIPtr(T* realPtr = 0);
   RCIPtr(const RCIPtr& rhs);
   ~RCIPtr();
   RCIPtr& operator=(const RCIPtr& rhs);
 
   T* operator->() const;
   T& operator*() const;
 
   RCObject& getRCObject()               // give clients access to
   { return *counter; }                  // isShared, etc.
 
 private:
   struct CountHolder: public RCObject {
     ~CountHolder() { delete pointee; }
     T *pointee;
   };
 
   CountHolder *counter;
   void init();
 };
 
 template<class T>
 void RCIPtr<T>::init()
 {
   if (counter->isShareable() == false) {
     T *oldValue = counter->pointee;
     counter = new CountHolder;
     counter->pointee = oldValue ? new T(*oldValue) : 0;
   } 
 
   counter->addReference();
 }
 
 template<class T>
 RCIPtr<T>::RCIPtr(T* realPtr)
 : counter(new CountHolder)
 { 
   counter->pointee = realPtr;
   init();
 }
 
 template<class T>
 RCIPtr<T>::RCIPtr(const RCIPtr& rhs)
 : counter(rhs.counter)
 { init(); }
 
 template<class T>
 RCIPtr<T>::~RCIPtr()
 { counter->removeReference(); }
 
 template<class T>
 RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs)
 {
   if (counter != rhs.counter) {         
     counter->removeReference();     
     counter = rhs.counter;
     init();
   }
   return *this;
 }
 
 template<class T>
 T* RCIPtr<T>::operator->() const
 { return counter->pointee; }
 
 template<class T>
 T& RCIPtr<T>::operator*() const
 { return *(counter->pointee); }
 
 
 /******************************************************************************
 *                          Class Widget (from p. 210)                         *
 *                                                                             *
 * This class is the same as that in the book, but I've added an               *
 * implementation so that RCIPtr can be tested.                                *
 ******************************************************************************/
 class Widget {
 public:
   Widget(int size): value(size) {}
   Widget(const Widget& rhs): value(rhs.value) {}
   ~Widget() {}
 
   Widget& operator=(const Widget& rhs)
   {
     value = rhs.value;
     return *this; 
   }
 
   void doThis()
   { value = -1; }
 
   int showThat() const
   { return value; }
   
 private:
   int value;
 };
 
 
 /******************************************************************************
 *                         Class RCWidget (from p. 210)                        *
 *                                                                             *
 * I modified this class for the 13th printing of the book.  For details on    *
 * why, consult the erratum affecting pages 209, 210, and 316 that was fixed   *
 * on 7/15/02 in the book's errata list.                                       *
 ******************************************************************************/
 class RCWidget {
 public:
   RCWidget(int size)
   : value(new Widget(size)) {}
 
   void doThis()
   {
     if (value.getRCObject().isShared()) {         // do COW if 
       value = new Widget(*value);                 // Widget is shared
     }
     value->doThis();
   }
 
   int showThat() const
   { return value->showThat(); }
 
 private:
   RCIPtr<Widget> value;
 };
 
 
 /******************************************************************************
 * Functions to perform VERY simple test of the above.                         *
 ******************************************************************************/  
 void testRCPtr()
 {
   String s1 = "String with no changes.";
   String s2(s1);
   
   s2[12] = s2[13] = ' ';
   
   std::cout << s1 << '\n';      // prints "String with no changes."
   std::cout << s2 << '\n';      // prints "String with    changes."
 }
 
 void testRCIPtr()
 {
   RCWidget w1(10);
   RCWidget w2(w1);
 
   w2.doThis();
   
   std::cout << w1.showThat() << '\n';       // prints 10
   std::cout << w2.showThat() << '\n';       // prints -1
 }
 
 int main()
 {
   testRCPtr();
   testRCIPtr();
 
   return 0;
 }