方案三
class String { private: char* _str; size_t _size; size_t _capacity; int* _refCount; //*** };
方案三設置了一個int型的指針變量用來引用計數,每份內存空間對應一個引用計數,而不是每個對象對應一個引用計數,而且每塊內存的引用計數互不影響,不會出現方案一和方案二出現的問題。
1.在實現賦值運算符重載要謹慎,不要遇到下圖的情形
2.改變字符串的某個字符時要謹慎,不要遇到類似下圖所遇到的問題。
如果多個對象都指向同一塊內存,那麼只要一個對象改變了這塊內存的內容,那所有的對象都被改變了!!
可以用下圖的形式改善這種問題:新設置一塊內存來存要改變的對象
案例3我畫的圖較多,方便大家結合代碼去理解
class String { public: String(char* str = "") //不能strlen(NULL) { _refCount = new int(1); //給_refCount開闢空間,並賦初值1 _size = strlen(str); _capacity = _size + 1; _str = new char[strlen(str) + 1]; strcpy(_str, str); } String(const String &s) { _refCount = s._refCount; _str = s._str; _size = strlen(s._str); _capacity = _size + 1; (*_refCount)++; //拷貝一次_refCount都要加1 } //要考慮是s1=s2時,s1原先不爲空的情況,要先釋放原內存 //如果要釋放原內存時,要考慮它的_refCount減1後是否爲0,爲零再釋放,否則其它對象指針無法再訪問這片空間 String& operator=(String& s) { if (_str!= s._str) { _size = strlen(s._str); _capacity = _size + 1; if (--(*_refCount) == 0) { delete[] _str; delete _refCount; } _str = s._str; _refCount = s._refCount; (*_refCount)++; } return *this; } //如果修改了字符串的內容,那所有指向這塊內存的對象指針的內容間接被改變 //如果還有其它指針指向這塊內存,我們可以從堆上重新開闢一塊內存空間, //把原字符串拷貝過來 //再去改變它的內容,就不會產生鏈式反應 // 1.減引用計數 2.拷貝 3.創建新的引用計數 char& String::operator[](const size_t index) //參考深拷貝 { if (*_refCount==1) { return *(_str + index); } else { --(*_refCount); char* tmp = new char[strlen(_str)+1]; strcpy(tmp, _str); _str = tmp; _refCount = new int(1); return *(_str+index); } } ~String() { if (--(*_refCount)== 0) //當_refCount=0的時候就釋放內存 { delete[] _str; delete _refCount; _str = NULL; cout << "~String " << endl; } _size = 0; _capacity = 0; } private: char* _str; //指向字符串的指針 size_t _size; //字符串大小 size_t _capacity; //容量 int* _refCount; //計數指針 };
以下是其它方案鏈接地址:
方案一:
http://iynu17.blog.51cto.com/10734157/1755179
方案二:
http://iynu17.blog.51cto.com/10734157/1755185
方案四:(推薦)
http://iynu17.blog.51cto.com/10734157/1755213