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.
它依然以浅拷贝实现为主,以指针的值传递,但是它多加了引用计数,当多个对象指向同一块空间,引用计数则统计对象个数,当只是读的时候就不用开辟新的内存,析构时根据引用计数值是否为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.
相关文章推荐
- 【C++】面试题:模拟实现string类(版本一:用深度拷贝来实现 )
- 【C++】浅析浅拷贝,深拷贝及写时拷贝(copy_on_write),模拟实现String类。
- 模拟实现String类(1)_深拷贝
- C++中String类模拟实现以及深拷贝浅拷贝
- 【C++】模拟实现string类(版本二:用写时拷贝来实现)
- String类深拷贝的模拟实现
- 模拟实现String类---->写时拷贝
- 【C++】模拟string类的实现(string 类的深拷贝)
- 模拟实现String类(2)——写时拷贝
- 详解C++中String类模拟实现以及深拷贝浅拷贝
- 请用c++ 实现stl中的string类,实现构造,拷贝构造,析构,赋值,比较,字符串相加,获取长度及子串等功能
- c语言内存管理函数,模拟memcpy、memmove(实现内存重叠拷贝)、memset
- C++【拷贝构造】和【拷贝赋值】(实现自定义的string类)
- 模拟实现String类----传统写法
- C++ 中string类的三种模拟实现方式
- 简单的String类实现及写时拷贝
- C++ 中string类的三种模拟实现方式
- 模拟实现string类
- C++【常见面试题】String类的实现,以及深拷贝、浅拷贝问题
- String类的实现与深浅拷贝问题