1.什么是写时拷贝
介绍写时拷贝之前,我们得先了解一下深浅拷贝,浅拷贝简单来说就是:只是对指针的拷贝,拷贝后和原来的指针指向同一块空间,如图:
所以,浅拷贝其实存在很多问题:
1.由于都指向同一块空间,所以,一旦其中一个对其内容做了更改,则所有对象都会发生变化,显然这并不是我们想要的。
2.很容易造成同一块空间被释放两次,因为都指向同一块空间。
3.很容易造成内存泄漏
深拷贝即不止对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
深拷贝解决了浅拷贝存在的那些问题,但是如果每次都给对象开辟内存,但是不一定会进行更改,岂不是会浪费内存?所以写时拷贝出现了
那么写时拷贝是什么意思呢?
顾名思义,就是在程序要进行写入操作是进行深度拷贝,否则进行浅拷贝,设置一个引用计数(与空间绑定在一起),当又有新指针指向这块空间时,引用计数+1,析构时,先检测引用计数是否为1,若为1则直接释放,否则,引用计数-1即可。有兴趣的读者可以验证一下,在Windows下,string是用深度拷贝的方式实现的,但是在Linux下,string是用写时拷贝的方式实现的。
2.写时拷贝string的部分实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstring>
using namespace std;
class String
{
friend ostream &operator<<(ostream &os, const String & s);
public:
String(char *str = "")
:_str(new char [strlen(str) + 5]) //将引用计数器和字符串放在同一块空间
{
_str += 4;
GetCount()=1; //取到引用计数器的空间并赋为1.
strcpy(_str, str);
}
~String()
{
Release(); //为增强代码的复用性,封装了一个释放空间的函数
}
String(const String & s)
:_str(s._str)
{
GetCount()++;
}
String& operator=( String& s)
{
if (this != &s) //判断是否自己给自己赋值
{
Release();
_str = s._str;
++GetCount();
}
return *this;
}
char &operator[](size_t index) //写时,深度拷贝
{
if (GetCount() > 1)
{
GetCount()--;
char *tmp = new char[strlen(_str) + 5];
strcpy(tmp+4, _str);
_str = tmp+4;
GetCount()++;
}
return _str[index];
}
private:
int& GetCount() //得到引用计数空间并以引用的方式返回,方便计数器改变
{
return *(int *)(_str - 4);
}
void Release()
{
if (GetCount() == 0)
{
delete[](_str - 4);
}
}
private:
char* _str;
};
ostream &operator<<(ostream &os, const String & s)
{
os << s._str << "";
return os;
}
void test1()
{
String s1("hello");
String s2(s1);
String s3("world");
s2 = s3;
}
void test2()
{
String s("hello");
s[2] = 'q';
cout << s << endl;
}
int main()
{
test1();
test2();
getchar();
return 0;
}