写时拷贝

什么是写时拷贝?

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值