Counted body

Problem Naive implementations of assignment in C++ are often inefficient or incorrect.

Context A design has been transformed into body/handle C++ class pairs (Handle/Body pattern). The pattern may be relevant to other object-based programming languages.

Forces Assignment in C++ is defined recursively as member by-member assignment with copying as the termination of the recursion; it would be more efficient and more in the spirit of Smalltalk if copying were rebinding.
  • Copying of bodies is expensive.
  • Copying can be avoided by using pointers and references, but these leave the problem of who is responsible for cleaning up the object and leave a user-visible distinction between built-in types and user-defined types.
  • Sharing bodies on assignment is usually semantically incorrect if the shared body is modified through one of the handles.

Solution
  • A reference count is added to the body class to facilitate memory management.
  • Memory management is added to the handle class, particularly to its implementation of initialization, assignment, copying, and destruction.
  • It is incumbent on any operation that modifies the state of the body to break the sharing of the body by making its own copy. It must decrement the reference count of the original body.
Forces resolved
  • Gratuitous copying is avoided, leading to a more efficient implementation.
  • Sharing is broken when the body state is modified through any handle. Sharing is preserved in the more common case of parameter passing, etc.
  • Special pointer and reference types are avoided.
  • Smalltalk semantics are approximated; garbage collection is driven off of this model.
Design rationale Reference counting is efficient and spreads the overhead across the execution of real-time programs. This implementation is a variation of shallow copy with the semantics of deep copy and the efficiency of Smalltalk name-value pairs.

Example
class String {
private:
    class StringRep {
        friend class String;
        int    count;
        char  *rep;
        StringRep (const char *s) : count(1)
        {
            strcpy(rep=new char[strlen(s)+1], s);
        }
	~StringRep() { delete rep; }
    } *rep;
public:
    String() : rep(new StringRep("")) {}
    String(const String &s): rep(s.rep)
        { rep->count++; }
    String& operator= (const String &s) {
        s.rep->count++;
        if(--rep->count <= 0) delete rep;
        rep = s.rep;
        return *this;
    }
    ~String() {
        if (--rep->count <= 0) delete rep;
    }
    String(const char *s) : rep(newStringRep(s)) {}
    ....
};

int main() {
    String a= "hello", b = "world";
    a=b;
    return 0;
}

[Source: James Coplien, "Setting the stage", C++ Report, Oct 94, p16]