Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 5.  Object Access Models


5.4. Shared Objects

Where it is suitable, this is my favorite access model and is based around reference counting (see Listing 5.5). The container holds a reference count on the contained items and provides reference-counted pointers to the items upon request.

Listing 5.5.


class EnvironmentVariable


{


public:


  typedef ref_ptr<RCString>  String_ref_type;


// Accessors


public:


   . . .


  String_ref_type GetValuePart(size_t index)


  {


    return index < m_valueParts.size()


              ? m_valueParts[index]


              : String_ref_type(0);


  }


// Members


private:


  Vector<String_ref_type> m_valueParts;


};



With a suitably competent reference counting smart-pointer class, which we'll assume our notional ref_ptr to be, it's ridiculously easy. The semantics of the ref_ptr ensure that when copies are taken, the reference count is increased, and when instances are destroyed, the reference count is decreased. When the reference count reaches 0, the RCString instance deletes itself.

This does incur a performance cost, which is generally low, but not so low as to be insignificant in very high-performing systems. Herb Sutter reports in [Sutt2002] on the results of performance tests of strings that implement reference counting and copy-on-write, which demonstrate that the reference counting can often end up being a pessimization [Sutt2002]. The scenario I've shown here is a little different, whereby it's the string object itself that is reference counted, so the performance effects may differ, but it's important to be aware of the costs of the different access models.

Despite these caveats, this model is a very powerful technique, and needn't be hindered by performance concerns. I have used a reference-counting scheme on a high-speed data parser that runs on several platforms, and outperforms the previous version by an order of magnitude.[1]

[1] I must be honest and admit that this was the same system that I mention in section 22.6 whose first version spent 15 percent of CPU on doing nothing due to an "oversight" on my part.

This solution can even work for types that have not been built for reference counting by using a smart pointer type that brings reference-counting logic with it, such as Boost's shared_ptr. Furthermore, a reference-counted approach can be used to remove the dependency of a contained object's lifetime on that of its (original) container. For example, consider you have a component that enumerates the contents of the file system. If it uses reference counting for the data objects it uses to represent file-system entries, client code would be able to hold on to these objects for as long as they need them, rather than having to take copies of all particular file attributes they potentially need before the enumerating component is destroyed. This can have substantial cost savings.


      Previous section   Next section