在我们进行字符串复制时用的都是深拷贝,如果用浅拷贝会导致double free问题,这时因为我们两个指针指向了同一块内存空间,当进行析构函数释放内存空间时就会同一块内存释放两次。但是由于深拷贝的效率要比浅拷贝慢很多,如果我们只是去读数据时我们可以只用浅拷贝,等到写数据时用深拷贝,这就是写时复制。
class String
{
public:
String(const String& rhs)
:_pstr(new char[strlen(rhs._pstr)+1])
{
strcpy(_pstr,rhs._pstr);
}
private:
char* _pstr;
};
当我们只是去读数据时我们可以只用浅拷贝让所有的指针都指向那一块数据区域。当释放内存时等到只有一个指针指向“Hello,World”时,才用delete将该快内存释放。如何判断指向“Hello,World”这块内存的指针个数呢?我们可以用引用计数,只要有一个指针指向该块内存我们就加一,当要释放时只需判断该引用计数是否为1即可。
当我们要写数据时,就进行深拷贝,创建一块新的堆空间将str5指向这块堆空间。这就是写时拷贝。
代码实现
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
using std::cout;
using std::cin;
using std::endl;
using std::vector;
//写时复制
//引用计数的位置
//1,栈空间error
//2.全局静态error
//堆空间
class String
{
public:
String(const String& rhs)
:_pstr(rhs._pstr)
{
*(int*)(_pstr - 4)+=1;
}
String()
:_pstr(new char[5]() +4)
{
*(int*)(_pstr - 4)=1;
}
String(const char* str)
:_pstr(new char[strlen(str)+5]() +4)
{
strcpy(_pstr,str);
*(int*)(_pstr - 4)=1;
}
//b=a;
String& operator=(const String& rhs)
{
cout<<"String& operator=(const String& rhs)"<<endl;
if(&rhs != this)
{
*(int*)(_pstr - 4)-=1;
if(*(int*)(_pstr - 4) == 0)
{
delete[] (_pstr-4);
}
_pstr = rhs._pstr;
*(int*)(rhs._pstr - 4)+=1;
}
return *this;
}
~String()
{
cout<<"~String()"<<endl;
*(int*)(_pstr - 4)-=1;
if(*(int*)(_pstr - 4) == 0)
{
delete[] (_pstr-4);
_pstr=nullptr;
}
else
{
_pstr=nullptr;
}
}
int getIndex() const
{
return *(int*)(_pstr - 4);
}
//底层字符串指针
const char* c_str() const
{
return _pstr;
}
size_t size()const
{
return strlen(_pstr);
}
friend std::ostream& operator<<(std::ostream &os,const String& rhs);
class CharProxy
{
public:
CharProxy(String& self,size_t idx)
:_self(self)
,_idx(idx)
{
}
//写操作
char& operator=(const char& ch);
//读操作
friend std::ostream& operator<<(std::ostream& os, const String::CharProxy& ch);
private:
//在String类中构造CharProxy,其中String是一个不完整数据类型,所以只能加上引用
String& _self;
size_t _idx;
};
CharProxy operator[](size_t idx)
{
return CharProxy(*this,idx);
}
friend std::ostream& operator<<(std::ostream& os, const String::CharProxy& ch);
private:
char* _pstr;
};
std::ostream& operator<<(std::ostream &os,const String& rhs)
{
os<<"pstr = "<<rhs._pstr<<endl;
return os;
}
//写操作CharProxy = 'H';
char& String::CharProxy::operator=(const char& ch)
{
cout<<"char& operator[](size_t n)"<<endl;
if(_idx<_self.size())
{
if(_self.getIndex()>1)
{
*(int*)(_self._pstr-4)-=1;
char *newpstr=new char[strlen(_self._pstr)+5]()+4;
strcpy(newpstr,_self._pstr);
*(int*)(newpstr-4)=1;
_self._pstr=newpstr;
newpstr=nullptr;
//return _self._pstr[_idx];
}
_self._pstr[_idx]=ch;
return _self._pstr[_idx];
}
else
{
static char charNull='\0';
return charNull;
}
}
//读操作
std::ostream& operator<<(std::ostream& os, const String::CharProxy& ch)
{
os<<ch._self._pstr[ch._idx];
return os;
}
int main()
{
String str("hello,world");
String str1,str3;
String str2=str;
printf("str = %p , str.index() = %d\n",str.c_str(),str.getIndex());
cout<<str<<endl;
printf("str1 = %p , str1.index() = %d\n",str1.c_str(),str1.getIndex());
cout<<str1<<endl;
printf("str2 = %p , str2.index() = %d\n",str2.c_str(),str2.getIndex());
cout<<str2<<endl;
str[0]='H';
printf("str = %p , str.index() = %d\n",str.c_str(),str.getIndex());
cout<<str<<endl;
printf("str1 = %p , str1.index() = %d\n",str1.c_str(),str1.getIndex());
cout<<str1<<endl;
printf("str2 = %p , str2.index() = %d\n",str2.c_str(),str2.getIndex());
cout<<str2<<endl;
cout<<"str[0]读数据"<<endl;
str1=str;
str2=str;
str3=str;
cout<<"str[0] = "<<str[0]<<endl;
printf("str1 = %p , str1.index() = %d\n",str1.c_str(),str1.getIndex());
cout<<str1<<endl;
printf("str2 = %p , str2.index() = %d\n",str2.c_str(),str2.getIndex());
cout<<str2<<endl;
printf("str = %p , str.index() = %d\n",str.c_str(),str.getIndex());
cout<<str<<endl;
}