引用计数
String s1("abcdef");
String s2(s1);
String s3(s1);
String s4("hello");
s1 = s4;
除初始化对象以外,每个构造函数(拷贝构造除外)需建立一个引用计数,用来记录多少对象与正在创建对象共享一块内存空间,当有新的指针指向这块空间时,引用计数加1。当创建一个对象且此对象未与其他对象共用一块内存空间时,将引用计数初始化为1。
拷贝构造不分配新的引用计数,而是拷贝计数器,并递增计数器。
析构函数递减计数器,每次递减代表该共用空间的成员少一个,当计数器减到0时,释放该空间。
赋值运算符重载时,递减左侧对象的计数器,递增右侧对象的计时器。若左侧对象计数器为0,则需销毁该对象。
写时拷贝
写时拷贝是在需要修改对象时才真正去开辟空间拷贝数据,如果只是读数据,则只需浅拷贝即可。
写时拷贝是通过引用计数实现的,当有对象需要修改其指向空间的值时,则需要重新开辟空间,保证修改此对象的值不会影响其他对象的值,此时要将旧空间的引用计数减1,新空间的引用计数加1。
String类中的两种写时拷贝:
- 引用计数单独开辟空间
class String
{
public:
String(char* str = "")
:_str(new char[strlen(str) + 1])
, _pCount(new size_t(1))
{
strcpy(_str, str);
}
// s2(s1)
String(const String& s)
:_str(s._str)
, _pCount(s._pCount)
{
(*_pCount)++;
}
//s2 = s1
String& operator=(const String& s)
{
if (this != &s)
{
if (--(*_pCount) == 0)
{
delete[] _str;
delete _pCount;
}
_str = s._str;
_pCount = s._pCount;
(*_pCount)++;
}
return *this;
}
~String()
{
if (--(*_pCount) == 0)
{
delete[] _str;
delete _pCount;
}
}
const char* c_str()
{
return _str;
}
//减引用计数、拷贝、创建新的引用计数
void CopyOnWrite()
{
if ((*_pCount)-- > 1)
{
char* tmp = new char[strlen(_str) + 1];
strcpy(tmp, _str);
_str = tmp;
_pCount = new size_t(1);
}
}
//如果指向该空间的指针只有一个,直接进行修改
//如果指向该空间的指针数大于一,则调用写时拷贝开辟空间
char& operator[](size_t pos)
{
if (*_pCount > 1)
{
CopyOnWrite();
}
return _str[pos];
}
private:
char* _str;
size_t* _pCount;
};
- 多开辟4个字节用来存放引用计数
class String
{
public:
String(char* str = "")
:_str(new char[strlen(str) + 4 + 1])
{
if (_str == NULL)
{
*(int*)_str = 1;
*(_str + 4) = '\0';
}
else
{
*(int*)_str = 1; //前四个字节存放引用计数
_str += 4;
strcpy(_str, str);
}
}
int& GetCount()
{
return *((int*)(_str - 4));
}
// s2(s1)
String(const String& s)
:_str(s._str)
{
++GetCount(); //向前偏移四个字节到引用计数,将计数器加1
}
//s2 = s1
String& operator=(const String& s)
{
if (this != &s)
{
if (--GetCount() == 0)
{
delete[] (_str - 4);
_str = NULL;
}
_str = s._str; //指向新空间
++GetCount(); //将新空间的计数器加1
}
return *this;
}
~String()
{
if (_str == NULL)
{
return;
}
else
{
if (--GetCount() == 0)
{
delete [] (_str - 4);
_str = NULL;
}
}
}
const char* c_str()
{
return _str;
}
void CopyOnWrite()
{
if (GetCount()-- > 1)
{
char* tmp = new char[strlen(_str) + 4 + 1];
tmp += 4;
strcpy(tmp, _str);
_str = tmp;
GetCount() = 1;
}
}
char& operator[](size_t pos)
{
if (GetCount() > 1)
{
CopyOnWrite();
}
return _str[pos];
}
private:
char* _str; // 引用计数在头上
};