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;
}

