C++:string类写时拷贝|引用计数
✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛)
🌈 个人Motto:他强任他强,清风拂山冈!
💫 欢迎来到我的学习笔记!
写时拷贝(了解)
参考博客:C++写时拷贝的不同方案(String类)_c++ string 从第三个字节拷贝-CSDN博客
1.1 概念
- 在此之前,我们就有讲过深浅拷贝的问题
- 浅拷贝会导致同一块空间析构两次;
- 一个对象进行修改等变化会影响另一个对象(因为同一个地址同一块空间)
- 因此我们使用深拷贝解决该问题:先开辟空间后拷贝数据。但是这种方法需要我们没创建一个对象就去深拷贝,可在后面该对象并没有做任何修改操作,那么深拷贝的作用就不大了,而且还浪费了内存空间资源。(深拷贝:每次对对象进行值和空间同时拷贝,但是会使用更多的空间)
- 因此就有了写时拷贝。当对空间进行修改时,检查出来自己以外是否还有其他对象使用此空间。有,自己重新开辟空间进行修改,不去影响其他对象;没有,则表示是自己空间资源独享,直接进行更改。而这里引入引用计数,用于统计有多少对象在使用这块空间资源。
1.2 方案1
- 当创建出一个对象s1,利用s1拷贝构造出一个对象s2,引用技术
refCount
自动+1,此时值为2。 - 如果现在要对s2进行自身的修改操作,先判断
refCount
是否大于1。若大于1,则s2要重新开辟一块空间,然后再进行修改操作,避免影响其他对象。
- s2重新开辟了一块空间,然后再修改原来的引用计数
refCount
,使其-1;重新开辟的空间中因为此时只有一个对象,所以引用计数refCount
也是1。
总结:此方案的写时拷贝技术是同时开辟两块空间,一块用于存放自身的内容,另一块用于存放引用计数refCount
,同时管理两块空间,统计当前使用此空间的对象数,当要修改当前空间的时候,进行引用计数的判断,再决定是否开辟新空间。
class String
{
public:
//构造函数
String(char* str="")
:_str(new char[strlen(str)+1])
,_refCount(new int(1))
{
strcpy(_str, str);
}
//拷贝构造函数
String(String& s)
{
_str = s._str;
_refCount = s._refCount;
++(*_refCount);
}
String& operator=(const String& s)
{
if (_str != s._str)
{
Release();
_str = s._str;
_refCount = s._refCount;
(*_refCount)++;
}
return *this;
}
void Release()
{
if (--(*_refCount) == 0)
{
cout << "delete" << endl;
delete[] _str;
delete _refCount;
}
}
~String()
{
Release();
}
void CopyOnWrite()
{
if (*_refCount > 1)
{
char* tmp = new char[strlen(_str) + 1];
strcpy(tmp, _str);
--(*_refCount);
_str = tmp;
_refCount = new int(1);
}
}
char& operator[](size_t pos)
{
CopyOnWrite();
return _str[pos];
}
char operator[](size_t pos) const
{
return _str[pos];
}
private:
char* _str;
int* _refCount;
};
1.3 方案2
- 开辟两块空间同时进行管理,方案2只开辟一块空间进行写时拷贝操作。
- 在对对象进行操作时,先检查引用计数的个数,然后再判断是否开辟新的空间,同时修改引用计数的值,防止空间不能释放。
- 例如:当创建的3个对象
s1、s2、s3
同时指向一个空间时,再创建一个对象s4
,s4的引用计数为1。
再进行s3 = s4
操作,此时对应的引用计数和对应的指向都需要进行修改,更改后图形如下:
- 此时对象
s3
指向了s4
,同时原来的空间的引用计数进行-1
操作 ,新指向空间的引用计数进行+1
操作,而且不用开辟两块空间。