条款14:缓式优化,之二:(2)
在析构函数中,我们要记得对引用计数进行管理,因为可能有其他的String对象正在共享相同的StringBuf实体。如果其他String对象还在使用这个StringBuf,我们就无须理会它,只用递减引用计数以表明我们不再需要它,然后退出。但如果我们是StringBuf的最后一个客户,我们就要清除它:
- String::~String()
- {
- if ( --data_->refs < 1 ) // 最后一个
- {
- delete data_; // ...关灯(删除缓冲区)
- }
- }
仅有的另外一个需要修改引用计数的地方是复制构造函数,在那儿,为了实现"缓式复制"语义,我们只需简单地指向另一个String已有的StringBuf,并递增计数以记录我们的存在。这是一种"浅复制"。只是在需要对"共享这个缓冲区的其中一个字符串"进行修改时,我们才会分离这个实体(即,执行"深复制"):
- String::String( const String& other )
- : data_(other.data_)
- {
- ++data_->refs;
- }
为了提高代码的清晰性,我还另外实现了一个辅助函数AboutToModify();因为除了Append(),其他修改操作函数(mutators)也需要用到它。AboutToModify()确保我们拥有一个非共享的内部缓冲区复制--如果在此之前没有执行深复制,现在就会执行。为了方便,AboutToModify()还提供了一个参数,表示欲分配缓冲区的最小值,这样,我们就不会不必要地得到一个已经满载的字符串复制,然后又立即转过头去执行第二次分配以获得更大的空间。
- 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.get()->buf );
- newdata->used = data_->used;
-
- --data_->refs; // 现在,所有实际工作已经完成,
- data_ = Newdata.release(); // 所以,获得拥有权
- }
- else
- {
- data_->Reserve( n );
- }
- }
既然其他部分已经就绪,Append()就很简单了。和前面一样,它只是宣告将要修改字符串,从而保证物理字符串缓冲区不被共享,而且其大小足以容纳另外一个字符。然后,它继续前进,执行修改操作:
- void String::Append( char c ) {
- AboutToModify( data_->used+1 );
- data_->buf_[data_->used++] = c;
- }
- }
所有的工作就这么多。下一步,我们将使接口更丰富一些,并看看这会带来什么变化。