C++编程惯用方法(1)

一. 使用引用计数来避免多分拷贝

 

class String
{
private:
 class String_rep
 {
 public:
  int use_count;
  char* chars;
  
  String_rep(const char*);
  String_rep(const String_rep&);
  ~String_rep();
  
  void increment();
  void decerement();
  const String_rep& operator=(const String_rep&); 
 };
 String_rep* rep;
public:
 String(const char* = "");
 ~String();
 const String& operator=(const String&);
};

此处String_rep是一个嵌套类:它声明在String类中。由于我们将它声明在String的使用段中,所以它只能被 String 的成员调用。

也许有的编译器不支持嵌套类,可以用如下方法:将String_rep声明为非嵌套类,它里面的所有成员(包括构造函数)都被声明为
私有,然后将 String 类声明为他的友类:

class String_rep
{
private:
 friend class String;
 
 int use_count;
 char* chars;
 
 String_rep(const char*);
 String_rep(const String_rep&);
 ~String_rep();
 
 void increment();
 void decerement();
 const String_rep& operator=(const String_rep&); 
};

这样可以保证用户无法构造 String_rep 的对象,但带来的弊端是:我们必须把String_rep放置到全局的名字空间中去。


1.String_rep 的实现

String_rep 的构造函数很简单:它会为字符串申请空间,将字符串复制到该空间中去,然后把计数器置为0.它的析构函数
会释放掉构造函数中所申请的内存空间。

String::String_rep::String_rep(const char* cp)
:use_count(0)
,chars(new char[strlen(cp) + 1])
{
 strcpy(chars, cp);
}

String ::String_rep::~String_rep()
{
 delete[] chars; 
}

void String::String_rep::increment()
{
 ++use_count;
}

void String::String_rep::decerement()
{
 if(--use_count == 0){
  delete[] this;
 } 
}

由于 increment() 和 decerement() 这两个方法都很小,且经常使用,最好将它们声明为 inline 函数。


String::String_rep::String_rep(const String_rep& orig)
:use_count(0)
,chars(new char[strlen(orig.chars) + 1])
{
 strcpy(chars, orig.chars);
}

const String::String_rep& String::String_rep operator=(const String_rep& orig)
{
 if(this != &orig){
  delete [] this;
  
  chars = new char[strlen(orig.chars) + 1];
  strcpy(chars, orig.chars); 
 }
 return *this;
}


2.String 的实现

在String类中,必须维护String_rep中的使用计数器。这包含两部分:在创建String对象时调用increment();
以及在摧毁String时调用decerement();

String::String(const char* c)
:rep(new String_rep(c))
{
 rep->increment();
}

String::~String()
{
 rep->decerement(); 
}

String::String(const String& s )
:rep(s.rep)
{
 rep->increment();
}

任何改变 String::rep 值的操作都必须对计数器进行同时更新:
const String& String::operator=(const String& s)
{
 if(rep != s.rep){
  rep->decerement();
  
  rep = s.rep;
  rep->increment(); 
 } 
}

我们必须在使用赋值操作符时仔细检测 s = s 这种情况,以避免该操作中调用的 decerement()会将 rep 删除。


3.写时复制(Copy on write)

两个或多个String 共享同一个rep的事实只是我们实现中的细节;我们必须确保所有会导致String值改变的操作
不会导致其他共享该rep 的String对象的值也发生改变。
解决方法:在这种操作中分配一个新的 rep,让它来维护被更改后的值。

#include <ctype.h>

void String::to_lower()
{
 if(rep->use_count > 1){
  String_rep* new_rep = new String_rep(rep->chars);
  rep->decerement();
  rep = new_rep;
  rep->increment(); 
 } 
 assert(rep->use_count == 1);
 for(char* cp = rep->chars; *cp; ++cp){
  *cp = tolower(*cp);
 }
}

注意:
使用了计数器的类要比没有使用计数器但功能相同的类要复杂的多,并且所有围绕着该计数器的操作都导致程序执行的时间显著增加。
经比较的结论是:使用了计数器的版本在某些程序(其中对String的平均赋值超过一次)中要快一些,在其他程序中则要慢些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值