Solution

I l @ ve RuBoard

graphics/bulb.gif

First, let's consider StringBuf . Note that the default memberwise copying and assignment don't make sense for StringBuf s, so both operations are suppressed (declared as private and not defined).

Optimized::StringBuf now does part of the work that the Original::String did for itself. StringBuf 's default construction and destruction are just what you'd expect:

 namespace Optimized {   StringBuf::StringBuf()       : buf(0), len(0), used(0), refs(1) { }     StringBuf::~StringBuf() { delete[] buf; } 

Reserve() too is familiar from our Original::String :

 void StringBuf::Reserve( size_t n ) {   if( len < n )   {     size_t newlen = max( len * 1.5, n );     char*  newbuf = new char[ newlen ];     copy( buf, buf+used, newbuf );     delete[] buf;   // now all the real work is     buf = newbuf;   //  done, so take ownership     len = newlen;   } } 

That's it for StringBuf .

Next , consider String itself. The default constructor is easy to implement:

 String::String() : data_(new StringBuf) { } 

In the destructor, we have to remember to manage the reference count because there might be other String objects sharing the same StringBuf representation. If other String s are still using the StringBuf , we leave it alone and just go away after decrementing the usage count to note that we don't care about it anymore. But if we're the last client of the StringBuf , we clean it up:

 String::~String() {   if( --data_->refs < 1 )  // last one out ...   {     delete data_;  // ... turns off the lights   } } 

The only other place we have to change the reference count is in the copy constructor, which we'll write to implement the lazy copy semantics by simply pointing at the other String 's existing StringBuf and bumping the count to record our presence. This is a " shallow " copy. We will only split off the shared representation (that is, make a deep copy) if we discover that we need to modify one of the strings that share this buffer:

 String::String( const String& other )   : data_(other.data_) {   ++data_->refs; } 

I've chosen to implement an additional AboutToModify() helper function for code clarity, because it will be needed by other mutators besides Append() . AboutTo Modify() ensures that we have an unshared copy of the internal buffer ”it lazily performs the deep copy if it has not already been performed. For convenience, About ToModify() also takes a minimum buffer size hint so that we won't needlessly take our own copy of a full string only to turn around and immediately perform a second allocation to get more space.

 void String::AboutToModify( size_t n ) {   if( data_->refs > 1 )   {     auto_ptr<StringBuf> newdata( new StringBuf );     newdata->Reserve( max( data_->len, n ) );     copy( data_->buf, data_->buf+data_->used, newdata->buf );     newdata->used = data_->used;     --data_->refs;             // now all the real work is     data_ = newdata.release(); //  done, so take ownership   }   else   {     data_->Reserve( n );   } } 

Now that all the other machinery is in place, Append() is simple. As before, it just declares that it's about to modify the string, thereby ensuring both that the physical string buffer is unshared and that it's big enough to hold an additional character. Then it goes ahead and makes the change:

 void String::Append( char c ) {     AboutToModify( data_->used+1 );     data_->buf[used++">data_->used++] = c;   } } 

That's all there is to it. In our next step, we'll make the interface just a little richer and see what happens.

I l @ ve RuBoard


More Exceptional C++
More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions
ISBN: 020170434X
EAN: 2147483647
Year: 2001
Pages: 118
Authors: Herb Sutter

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net