C++String深浅拷贝、写时拷贝
在C++中我们要拷贝一个字符串的时,有俩种方法:
1.浅拷贝
2.深拷贝
① 浅拷贝:就是让当前的指针指向已存在的区域,和其他指针共同管理同一块空间
下面举一个String类中字符串str的浅拷贝
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
using namespace std;
class String
{
public :
String(const char* str)
:_str(new char[strlen(str)+1])
{
strcpy(_str,str);
}
String(const String& s)
:_str(s._str)
{
}
String& operator=(const String& s)
{
if(this!=&s)
{
_str=s._str;
}
return *this;
}
~String()
{
if(_str)
{
delete[] _str;
}
}
private:
char* _str;
};
void test()
{
String s("hello word!");
String s1(s);
}
int main()
{
test();
return 0;
}
在这的s._str和s1._str共同指向了同一块空间
因为他俩同时指向同一块空间,但析构的时候只会去找这块空间,会导致析构俩次而出错,这就是简单赋值浅拷贝的弊端。
②深拷贝
深拷贝:就是开辟一块和被拷贝对象同样大小的空间,并复制其内容
如图所示:
深拷贝代码如下:
class String
{
public:
String(const char* str)
:_str(new char[strlen(str)+1])
{
strcpy(_str,str);
}
String(const String& s)
:_str(new char[strlen(s._str)+1])
{
strcpy(_str,s._str);
}
String& operator=(const String& s)
{
if(this!=&s)
{
delete[] _str;
_str=new char[strlen(s._str)+1];
strcpy(_str,s._str);
}
return *this;
}
~String()
{
delete [] _str;
}
private:
char* _str;
};
void test()
{
String s("hello word!");
String s1(s);
String s2("hello!");
s2=s1;
}
int main()
{
test();
return 0;
}
写时拷贝(Copy—On—Write):
写时拷贝方法1:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
using namespace std;
class String
{
public :
String(const char* str)
:_str(new char[strlen(str)+1])
,_refcount(new int)
{
*_refcount=1;
strcpy(_str,str);
}
String(const String& s)
:_str(s._str)
{
_refcount=s._refcount;
(*_refcount)++;
}
String& operator=(const String& s)
{
if(_str!=s._str)//判断是否是自己给自己赋值
{
if(*refcount!=1)//判断是否有多个对象和它共用同一块空间,如是则进行计数指针的--操作
{
(*_refcount)--;//将计数指针的值减一下
}
_refcount=s._refcount;//将指针指向s
(*_refcount)++;//并对计数指针的值加加
}
return *this;
}
~String()
{
if(*_refcount==1)//当计数指针等于1时真正释放
{
delete[] _str;
}
else
(*_refcount)--;
}
private:
char* _str;
int* _refcount;
};
int main()
{
test();
return 0;
}
测试结果及分析:
当代码运行到String s2(“hello!”)时,可以清楚的看到s、s1指向同一块空间,*_refcount=2;s2是单独开辟了一块空间,*_refcount=1;
当代吗走到s2=s时,s、s1、s2指向同一块空间,*_refcount=3,只析构一次。
这种方法是采用一个计数指针来记下同一块空间被多少个对象共用,析构时只需将计数器减一次,当计数器等于1时,就真正要将这块空间释放了;同时当重载时只需对计数指针进行操作。
写时拷贝方法2:
这种方法和方法1其实是差不多的,只不多就是将计数器放在_str中。
class String
{
public:
String(char* str="")
:_str(new char[strlen(str)+5])
{
_str=_str+4;
strcpy(_str,str);
*((int*)_str-4)=1;
}
String(const String& s)
{
_str=s._str;
(*((int*)_str-4))++;
}
String& operator=(const String& s)
{
if(this!=&s)
{
if((*((int*)_str-4))==1)
{
delete[](_str-4);
}
_str=s._str;
(*((int*)_str-4))++;
cout<<"operator="<<endl;
}
return *this;
}
~String()
{
if((*((int*)_str-4))==1)
{
delete[] (_str-4);
cout<<"~String()"<<endl;
}
else
(*((int*)_str-4))--;
}
private:
char* _str;
};
void test()
{
String s("hello word!");
String s1(s);
String s2("hello!");
s2=s1;
}
int main()
{
test();
return 0;
}