什么是写时拷贝?
首先先说浅拷贝,就是拷贝的时候让当前的指针指向一块已存在的区域,和其他指针共享同一块地址空间。简单的浅拷贝所带来的问题是当程序结束的时候,对象d1和d2都会去调用析构函数清零这个快空间,而一块空间析构两次可能就会导致程序崩溃。(深浅拷贝参考http://blog.csdn.net/zhang1308299607/article/details/74933134)
虽说深拷贝可以很好的解决析构同一块地址空间带来的问题,但它也存在自身的问题。比如相同的数据内容却要每次都重新开辟空间去放置数据,这样会浪费空间。
写时拷贝综合了浅拷贝节省空间和深拷贝不会重复析构的优点。写时拷贝在浅拷贝的基础上加入了引用计数,当拷贝构造的时候只用把引用计数加1,析构的时候把引用计数减1,当引用计数为1的时候才真正去析构这块空间。
写时拷贝代码:
#define _CRT_SECURE_NO_DEPRECATE 1
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
using namespace std;
class String
{
public:
String(const char* str)
:_str(new char [strlen(str)+1])
,_size(strlen(str)+1) //考虑‘\0’在内 不考虑的话不加1即可
,_capacity(_size)
,_ref(new int(1))
{
strcpy(_str,str);
}
String(const String& d)
:_size(d._size)
,_capacity(d._capacity)
{
_str = d._str;
_ref = d._ref;
(*_ref)++;
}
~String()
{
if(*_ref == 1)
{
delete[] _str;
_str = NULL;
delete _ref;
_ref = NULL;
}
else
{
(*_ref)--;
}
}
//1判断是否是自己给自己赋值
//2 如果不是情况1 判断自身引用计数 如果是1则析构掉动态开辟的空间 否则只把引用计数减1.
//3然后把源数据指针赋值给目标指针 源引用计数++,再把_size和_capacity赋值给目标对象
String& operator=(const String& d)
{
if(_str == d._str)
{
return *this;
}
else
{
if(*_ref == 1)
{
delete[] _str;
_str = NULL;
delete _ref;
_ref = NULL;
}
else
{
(*_ref)--;
}
_str = d._str;
_ref = d._ref;
(*_ref)++; //size和_capacity没有改变?
_size = d._size;
_capacity = d._capacity;
}
return *this;
}
void PrintStringinfo();
void Expand(size_t inc);
void PushBack(char ch);
private:
char* _str;
int* _ref;
int _size;
int _capacity;
};
void String:: PrintStringinfo()
{
cout<<"data: "<<_str<<"\n"<<"ref: "<<*_ref<<"\n"<<"size: "<<_size<<"\n"<<"capacity: "<<_capacity<<"\n"<<endl;
}
void String:: Expand(size_t inc)
{
_str = (char*)realloc(_str,_size+inc);
if(_str == NULL)
{
perror(_str);
}
_capacity += inc;
}
//1重新开辟出来一块比源空间一样大小大1的空间
//2把源空间引用计数减一(考虑源引用计数只有1的情况 和源引用计数大于1的情况)
//2.1 如果引用计数等于1 直接在本空间扩容 else 另外开空间。
//3把字符放进去。
void String:: PushBack(char ch)
{
char* tmpptr = _str;
if((*_ref) > 1)
{
(*_ref)--;
_str = new char[_size+1];
strcpy(_str,tmpptr);
_str[_size] = '\0'; //往新开辟的空间里放‘\0’
_str[_size-1] = ch; //往原来'\0'的空间放 ‘ch’
_ref = new int;
(*_ref) = 1;
_size = strlen(_str)+1;
_capacity = _size;
}
else if((*_ref) == 1)
{
if(_capacity == _size)
{
Expand(sizeof(int));
}
strcpy(_str,tmpptr);
_str[_size] = '\0';
_str[_size-1] = ch;
_size = strlen(_str)+1;
}
}
测试用例:
int main()
{
String s1("abcd");
String s3(s1);
s3.PrintStringinfo();
system("pause");
return 0;
}
可以看出s1和s3的指针是指向同一块空间的,引用指针为2,程序正常结束
int main()
{
String s1("abcd");
String s3(s1);
s1.PushBack('e');
s1.PrintStringinfo();
s3.PrintStringinfo();
system("pause");
return 0;
}
PushBack的功能正常实现,其他功能没有写,大致思路一样。