String类的浅拷贝、深拷贝、引用计数、写时拷贝

时间:2022-12-06 19:50:56
皆以s2=s1为例

浅拷贝:只是直接将s1的值拷贝过来,s1、s2共用同一块内存。

class String
{
public:
String(const char* str)
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String(const String& s)
:_str(s._str)
{}
String& operator=(const String& s)
{
if (this != &s)
{
_str = s._str;
}
return *this;
}
~String()
{
if (_str)
{
cout << "delete" << endl;
delete[] _str;
}
}

private:
char* _str;
};

void TestString()
{
String s1("bigbang");
String s2(s1);
String s3("12345");
s3 = s1;
}
int main()
{
TestString();
system("pause");
return 0;
}
缺陷是调用析构函数时一块内存会被多次释放,导致程序崩溃。

String类的浅拷贝、深拷贝、引用计数、写时拷贝

深拷贝:在拷贝构造时开辟了空间,s1、s2各占有一块内存,无论析构多少次程序都不会崩溃。

//深拷贝传统写法
class String
{
public:
String(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& operator=(const String& s) //缺陷:new开辟空间可能会失败,s1将会被破坏
//{
//if (this != &s)
//{
//delete[] _str;
//_str = new char[strlen(s._str) + 1];
//strcpy(_str, s._str);
//}
//return *this;
//}
String& operator=(const String& s) //解决了上述缺陷
{
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}

~String()
{
if (_str)
{
cout << "delete" << endl;
delete[] _str;
_str = NULL;
}
}
private:
char* _str;
};

void TestString()
{
String s1("bigbang");
String s2(s1);
}
int main()
{
TestString();
system("pause");
return 0;
}

//深拷贝现代写法:只在构造函数中new,只在析构函数中delete,可维护性变强
class String
{
public:
String(char* str = "")
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String(const String& s)
:_str(NULL)
{
String tmp(s._str); //调用构造函数开辟临时空间,交换后可把原来的空间释放掉
swap(_str, tmp._str);
}

String& operator=(const String& s)
{
if (_str != s._str)
{
String tmp(s);
swap(_str, tmp._str);
}
return *this;
}
//String& operator=(String s) //针对已经存在的两个对象
//{
//swap(_str, s._str);
//return *this;
//}

~String()
{
if (_str)
{
cout << "delete" << endl;
delete[] _str;
_str = NULL;
}
}

private:
char* _str;
};

void TestString()
{
String s1("bigbang");
String s2(s1);
String s3;
s3 = s1;
}
int main()
{
TestString();
system("pause");
return 0;
}

在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。

class String
{
public:
String(char* str = "")
:_str(new char[strlen(str) + 1])
, _refCount(new int(1))
{
strcpy(_str, str);
}
String(const String& s)
:_str(s._str)
, _refCount(s._refCount)
{
++(*_refCount);
}

String& operator=(const String& s)
{
// 1.s1和s2是否指向同一块空间
// 2.减减s1指向空间的引用计数,如s1是最后一块管理对象,则释放
if (_str != s._str)
{
this->Release();
_str = s._str;
_refCount = s._refCount;
++(*_refCount);
}
return *this;
}
String& operator=(String s) //s是临时开辟出来的一块空间,出了函数会自动释放
{
swap(_str, s._str);
swap(_refCount, s._refCount); //交换后引用计数不改变
return *this;
}

~String()
{
Release();
}
void Release()
{
if (--(*_refCount) == 0)
{
cout << "delete" << endl;
delete[] _str;
delete _refCount; //注意delete的格式和new的格式对齐
}
}
private:
char* _str;
int* _refCount;
};


void TestString()
{
String s1("bigbang");
String s2(s1);
String s3;
s3 = s1;
}
int main()
{
TestString();
system("pause");
return 0;
}

//引用计数的浅拷贝--现代写法+写时拷贝:指用浅拷贝的方法拷贝其他对象,多个指针指向同一块空间,
//只有当对其中一个对象修改时,才会开辟一个新的空间给这个对象,和它原来指向同一空间的对象不会受到影响。
class String
{
public:
String(char* str = "")
:_str(new char[strlen(str) + 1])
, _refCount(new int(1))
{
strcpy(_str, str);
}
String(const String& s)
:_str(s._str)
, _refCount(s._refCount)
{
++(*_refCount);
}
String& operator=(const String& s)
{
if (_str != s._str)
{
this->Release();
_str = s._str;
_refCount = s._refCount;
++(*_refCount);
}
return *this;
}
String& operator=(String s)
{
swap(_str, s._str);
swap(_refCount, s._refCount);
return *this;
}
void Release()
{
if (--(*_refCount) == 0)
{
cout << "delete" << endl;
delete[] _str;
delete _refCount;
}
}
~String()
{
Release();
}

char& operator[](size_t index)
{
CopyOnWrite();

assert(index < strlen(_str));
return _str[index];
}
const char& operator[](size_t index) const
{
assert(index < strlen(_str));
return _str[index];
}
void CopyOnWrite()
{
if (*_refCount > 1)
{
char* tmp = new char[strlen(_str) + 1];
strcpy(tmp, _str);
--(*_refCount);

_refCount = new int(1);
_str = tmp;
}
}


private:
char* _str;
int* _refCount;
};

void Fun(const String& s)
{
cout << s[4] << endl;
}
void TestString()
{
String s1("bigbeng");
String s2(s1);
String s3 = s2;
Fun(s1);
s1[4] = 'a';
cout << s1[4] << endl;
}
int main()
{
TestString();

system("pause");
return 0;
}