string类写时拷贝的模拟实现

为了解决string类中浅拷贝的问题,在windows系统下它则以深拷贝来解决这一问题,由于深拷贝要不断的开辟内存空间,并花费时间,所以在linux/unix系统下则采用写时拷贝(Copy_On_Write)来实现:

它依然以浅拷贝实现为主,以指针的值传递,但是它多加了引用计数,当多个对象指向同一块空间,引用计数则统计对象个数,当只是读的时候就不用开辟新的内存,析构时根据引用计数值是否为1判断是否释放内存,而当写的时候则在开辟新的内存空间赋值拷贝,以免改变别的对象的内容。

实现原理如下:

1.读:在构造函数中开辟内存空间,多开辟4个字节,在前4个字节存放引用计数count,当一个对象指向这块内存时,count=1,当有另一个新的对象指向这块内存时,count++:


_str则需偏移4个字节指向内存真正的内容。

2.写:若要改变s2._str[1]的内容为q,此时实现如下:


代码模拟实现:

#pragma once
#include <string.h>
//string类的写时拷贝:以浅拷贝的方式实现,增加引用计数
class String
{
public:
	//构造函数
	String(const char* str="")
		:_str(new char[strlen(str)+5])//多开辟4个字节存放引用计数
	{
		(*((int*)_str))=1;//每构造一个对象将前4个字节赋值为1
		_str=_str+4;//内容拷贝则从引用计数4个字节后的位置开始
		strcpy(_str,str);
	}
	//拷贝构造函数
	String(const String& s)
		:_str(s._str)
	{
		(*((int*)(_str-4)))+=1;//多个对象指向一块空间,增加引用计数
	}
	//赋值运算符重载
	String& operator=(const String& s)
	{
		if(this!=&s)
		{
			if(--(*((int*)(_str-4)))==0)//若原空间只有一个对象使用
			{
				delete[] (_str-4);   //则释放,否则造成内存泄漏
			}
			_str=s._str;  //拷贝赋值
			(*((int*)(_str-4)))+=1;
		}
		return *this;
	}
	//析构函数
	~String()
	{
		if(--(*((int*)(_str-4)))==0)//当引用计数=0时,内存使用对象只有一个,析构释放
		{
			_str-=4;
			delete[] _str;
			_str=NULL;
		}
	}
	//返回字符串首地址
	char* C_str()
	{
		return _str;
	}
	//写时拷贝
	char& operator[](size_t index)
	{
		if((*((int*)(_str-4)))>1)//即至少有两个对象使用同一块空间
		{
			String temp(_str);
			std::swap(temp._str,_str);
		}
		return _str[index];
	}
private:
	char* _str;
};
void Test2()
{
	String s1("hello");
	String s2("world");
	String s3(s1);
	s3=s2;

	printf("s1地址:%x\n",(unsigned int)s1.C_str());
	printf("s2地址:%x\n",(unsigned int)s2.C_str());
	printf("s3地址:%x\n",(unsigned int)s3.C_str());
	cout<<endl;

	s3[1]='c';
	cout<<"改变s3:"<<endl;
	cout<<"s1:"<<s1.C_str()<<" s2:"<<s2.C_str()<<" s3:"<<s3.C_str()<<endl;
	printf("s3地址:%x\n",(unsigned int)s3.C_str());
	cout<<endl;

	s2[1]='b';
	cout<<"改变s2:"<<endl;
	cout<<"s1:"<<s1.C_str()<<"  s2:"<<s2.C_str()<<" s3:"<<s3.C_str()<<endl;
	printf("s2地址:%x\n",(unsigned int)s2.C_str());
	cout<<endl;

	s1[1]='a';
	cout<<"改变s1:"<<endl;
	cout<<"s1:"<<s1.C_str()<<"  s2:"<<s2.C_str()<<" s3:"<<s3.C_str()<<endl;
	printf("s1地址:%x\n",(unsigned int)s1.C_str());
}
测试运行结果如下:



注意:以上赋值运算符重载的实现,当原有对象引用计数为1时,要释放原有对象的内存空间,否则造成内存泄漏;当不为1时,要注意将原有对象所指空间的引用计数-1;最后当给赋值时,要注意在新的内存空间引用计数+1.


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值