您的位置:首页 > 其它

string类写时拷贝的模拟实现

2017-03-23 18:12 316 查看
为了解决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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: