概念:在有些大型类中复制一个类要花费大量时间。
如
TString one("abcd");
TString two(one);
这时写时复制就可以解决这个问题,所有复制对象共用原对象,当要改变对象的数据成员时才产生一个新的副本。
.head
1 #pragma once 2 #include<iostream> 3 #include<string> 4 #include<stdlib.h> 5 #include<ctype.h> 6 using namespace std; 7 8 class TString 9 { 10 public: 11 //输入/输出操作符 12 //构造函数,创建一个空字符对象 13 TString(); 14 //创建一个字符串对象,该对象包含指向字符的s指针 15 //s必须以NULL结尾,从s中复制字符 16 TString(const char* s); 17 //创建一个包含单个字符aChar的字符串 18 TString(char aChar); 19 TString(const TString& arg);//拷贝构造函数 20 ~TString();//析构函数 21 //赋值操作符 22 TString& operator=(const TString& arg); 23 TString& operator=(const char* s); 24 TString& operator=(char aChar); 25 //返回对象当前储存的字符个数 26 int Size() const; 27 //返回posn中len长度的子字符串 28 TString operator()(unsigned posn, unsigned len) const; 29 //返回下表为n的字符 30 char operator()(unsigned n) const; 31 //返回对下标为n的字符的引用 32 const char& operator[](unsigned n) const; 33 //返回指向内部数据的指针,当心 34 const char* c_str()const { return _str; }; 35 //以下方法将修改原始对象。 36 //把其他对象中的字符附加在*this后 37 TString& operator+=(const TString& other); 38 //在字符串中改动字符的情况 39 TString& ToLower();//将大写字符转换成小写 40 TString& ToUpper();//将小写字符转换成大写 41 private: 42 struct StringRep 43 { 44 char *_str;//实际的字符 45 unsigned _refCount;//对它的引用的数目 46 unsigned _length;//字符串中的字符数目 47 }; 48 StringRep *_rp; //再TString中唯一的数据成员; 49 50 }; 51 52 //支持TString类的非成员函数 53 //返回一个新TSring类的对象,该对象为one和two的级联 54 TString operator+(const TString& one, const TString& two); 55 //输入/输出操作符 56 ostream& operator<<(ostream& o, const TString& s); 57 istream& operator >> (istream& stream, TString& s); 58 //关系操作符,基于ASCII字符集比较。 59 //如果两字符串对象包含相同的字符,则两对象相等。 60 bool operator==(const TString& first, const TString& second); 61 bool operator!=(const TString& first, const TString& second); 62 bool operator<(const TString& first, const TString& second); 63 bool operator>(const TString& first, const TString& second); 64 bool operator>=(const TString& first, const TString& second); 65 bool operator<=(const TString& first, const TString& second);
.cpp;
1 #include "TString.h" 2 3 4 5 TString::TString() 6 { 7 _rp = new StringRep; 8 _rp->_refCount = 1; 9 _rp->_length = 0; 10 _rp->_str = 0; 11 } 12 13 14 TString::TString(const char* s) 15 { 16 _rp = new StringRep; 17 _rp->_refCount = 1; 18 _rp->_length = strlen(s); 19 _rp->_str = new char[_rp->_length + 1]; 20 strcpy(_rp->_str, s); 21 } 22 23 TString::TString(char aChar) 24 { 25 _rp = new StringRep; 26 _rp->_refCount = 1; 27 _rp->_length = 1; 28 _rp->_str = new char[_rp->_length + 1]; 29 _rp->_str[0] = aChar; 30 _rp->_str[1] = 0; 31 } 32 33 34 TString::TString(const TString& other) 35 { 36 //这时重要的操作之一; 37 //我们需要再other中,通过_rp所指向的对象递增引用计数。它又获得一个引用。 38 other._rp->_refCount++; 39 //让它们共享资源 40 this->_rp = other._rp; 41 } 42 43 TString& TString::operator=(const TString& other) 44 { 45 if (this == &other) 46 return *this;//自我赋值 47 /*这时另一个重要的操作。我们需要再other中,通过_rp所指向的对象递增引用计数。 48 同时,需要用过this指向的对象递减引用计数*/ 49 other._rp->_refCount++; //它又获得一个引用 50 //递减和测试,是否仍然再使用它 51 if (--this->_rp->_refCount == 0) 52 { 53 delete[]this->_rp->_str; 54 delete this->_rp; 55 } 56 this->_rp = other._rp;//共享资源; 57 return *this; 58 59 } 60 61 62 63 //这是一个重要的成员函数,需要应用“写时复制”方案 64 TString& TString::ToLower() 65 { 66 char *p; 67 if (_rp->_refCount > 1) 68 { 69 //这是最困难的部分。分离TString对象并提供它的StringRep对象; 70 //这是“写时复制”操作 71 unsigned len = this->_rp->_length;//保存它 72 p = new char[len + 1]; 73 strcpy(p, this->_rp->_str); 74 this->_rp->_refCount--;//因为*this即将离开内存池; 75 this->_rp = new StringRep; 76 this->_rp->_refCount = 1; 77 this->_rp->_length = len; 78 this->_rp->_str = p;//p在前面已创建 79 } 80 81 //继续,并改变字符 82 p = this->_rp->_str; 83 if (p != 0) 84 { 85 while (*p) 86 { 87 *p = tolower(*p); 88 ++p; 89 } 90 } 91 return *this; 92 } 93 94 TString& TString::ToUpper()//和tolower的相似方法 95 { 96 return *this; 97 } 98 99 100 101 TString::~TString() 102 { 103 if (--_rp->_refCount == 0) 104 { 105 delete[] _rp->_str; 106 delete _rp; 107 } 108 } 109 110 //省略其他函数的实现;
思考:在上面的代码中,很多地方都需要创建、删除和操控StringRep对象。很明显,这并不是最好的方法。尝试修改实现,以便StringRep有自己的构造
函数、析构函数以及其他函数。这样,StringRep便可自我管理。另外,完成TString类的实现;