写时拷贝

来源:互联网 发布:linux下utf8转gbk 编辑:程序博客网 时间:2024/06/06 07:25

什么是写时拷贝?

      首先先说浅拷贝,就是拷贝的时候让当前的指针指向一块已存在的区域,和其他指针共享同一块地址空间。简单的浅拷贝所带来的问题是当程序结束的时候,对象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的功能正常实现,其他功能没有写,大致思路一样。