方案三
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; //计数指针};
以下是其它方案链接地址:
方案一:
方案二:
方案四:(推荐)