浅拷贝:
class String { public: String(const char* str = "") :_str(new char[strlen(str)+1]) { strcpy(_str, str); } String(const String& s) :_str(s._str) {} private: char* _str; }; int main() { String s1("12345"); String s2(s1); system("pause"); return 0; }
这时,s1的_str和s2 的指向同一段空间,只把地址进行了拷贝,当改变s1的内容时,即使不想让s2发生变化,s2也发生了变化。
如果我们加上析构函数,用来释放我们申请的空间,程序就会崩溃。
~String() { delete[] _str; }
这是应为s1和s2指向了同一段空间,s2先析构,释放了申请的空间,s1析构时,对同一段空间再次进行释放(该空间已经释放了),对同一空间释放了2次,导致程序崩溃。
深拷贝:
为了解决上面出现的问题,可以重新申请一段空间,然后将需要拷贝的内容拷贝到自己的空间上。
class String { public: String(const char* str = "") :_str(new char[strlen(str)+1]) { strcpy(_str, str); } String(const String& s) :_str(new char[strlen(s._str)+1]) //重新申请一段空间 { strcpy(_str, s._str); //将字符串拷贝到该空间上 } ~String() { delete[] _str; } private: char* _str; }; int main() { String s1("12345"); String s2(s1); system("pause"); return 0; }
写时拷贝:
还有一种解决方法,就是加入引用计数,实现写时拷贝。意思就是,在浅拷贝的基础上加入引用计数,每当拷贝构造时,该引用计数++,直到需要进行写操作时,才重新开辟一段空间,让_str指针指向新开辟出来的空间。这样,就解决了浅拷贝重复析构以及改一个的同时也改变了其它字符串的问题。
class String { public: String(const char* str = "111") :_count(new int(1)) ,_size(strlen(str)) ,_capacity(_size) ,_str(new char[_size+1]) { strcpy(_str, str); } String(String& s) :_count(s._count) ,_size(s._size) ,_capacity(s._capacity) ,_str(s._str) { ++*_count; } String& operator=(const String& s) { if(--*_count == 0) { delete[] _str; delete _count; } _str = s._str; _count = s._count; ++*_count; _size = s._size; _capacity = s._capacity; return *this; } void CopyOnWrite(); void CopyOnWrite() { if(*_count > 1) { --*_count; char* tmp = new char[strlen(_str)+1]; int* count = new int(1); _str = tmp; _count = count; } return; } char& operator[](size_t pos); char& operator[](size_t pos) { CopyOnWrite(); return _str[pos]; }private: int* _count; size_t _size; size_t _capacity; char* _str; };
上面是给引用计数单独的开辟一个整型空间,也可以使用给_str多开辟4个字节的空间,让前四个字节来存储引用计数_count.
class String { public: String(const char* str = "") :_str(new char[strlen(str) + 5]) ,_size(strlen(str)) ,_capacity(_size) { _str += 4; strcpy(_str, str); *((int*)(_str-4)) = 1; } int& GetRefCount() { return *((int*)(_str-4)); } String(String& s) { if(--GetRefCount() == 0) { delete[] (_str - 4); } _str = s._str; _size = s._size; _capacity = s._capacity; ++GetRefCount(); } String& operator=(String& s) { if(_str != s._str) { if(--GetRefCount() == 0) { delete[] (_str-4); } _str = s._str; _size = s._size; _capacity = s._capacity; ++GetRefCount(); } return *this; } void CopyOnWrite() { if(GetRefCount() > 1) { --GetRefCount(); char* tmp = new char[strlen(_str-4)+1]; strcpy(tmp+4, _str); _str = tmp+4; GetRefCount() = 1; } } char& operator[](size_t pos) { CopyOnWrite(); return _str[pos]; } private: char* _str; size_t _size; size_t _capacity; };