大家首先来看一下下面这段代码:
void Test()
{
int *p1 = new int[10];
int *p2 = p1;
delete[] p1;
delete[] p2;
}
运行这段代码:
我们发现这段程序无法运行,经过测试我们发现,p1和p2指向同一块空间:
这就是我们所说的浅拷贝。
浅拷贝:也称位拷贝,编译器只是直接将指针的值拷贝过来,结果多个对象共用同一块内存,当一个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,所以在对这段内存进行操作的时候,发生了访问违规。
【深拷贝】
构造s2时拷贝一块跟s1指向数据块一样大的数据块,并将值拷贝下来,这样s1和s2指向各自的数据块,析构时释放各自的数据块。
String类实现深拷贝
普通写法:
String(const String& s)
{
_str = new char[strlen(s.str)+1];
strcpy(_str,s._str);
}
现代写法:
String(const String& s)
:_str(NULL)
{
String tmp(s._str);
swap(_str,tmp._str);
}
【写时拷贝】
class String
{
private:
char *_str;
static int refCount;
}
int String::refCount=0;
如此定义一个静态变量,会存在下面一个问题:
我们可以采用下面的方式进行定义:
class String
{
public:
String(const char* str=" ") //构造函数
:_str(new char [strlen(str)+1])
,_refCount(new int(1))
{
strcpy(_str,str);
}
String(String & s) //拷贝构造
:_str(s._str)
,_refCount(s._refCount)
{
(*_refCount)++;
}
~String() //析构函数
{
if(--(*refCount)==0)
{
delete[] _str;
delete _refCount;
}
}
private:
char *_str;
int *_refCount;
};
赋值运算符的重载我们需要考虑以下几种情况:
String& operator = (const String& s)
{
if(_str != s._str)
{
if(--(*_refCount)==0)
{
delete[] _str;
delete _refCount;
}
_str = s._str;
_refCount = s._refCount;
(*refCount)++;
}
return *this;
}
写时拷贝
void CopyWrite()
{
if(*refCount>1)
{
char* newStr = new char[strlen(_str)];
strcpy(newStr,_str);
(*_refCount)--;
_str = newStr;
_refCount = new int(1);
}
}
第二种实现方式:
String(const char* str = " ")
:_str(new char[strlen(str)+5]
{
strcpy(_str+4,str);
*((int*)_str) = 1;
_str += 4;
}
String(const String & s)
:_str(s._str)
{
(*(int*)(_str-4))++;
}
~String()
{
if(--*((int*)(_str-4))==0)
{
delete[](_str-4);
}
}
String & operator = (const String & s)
{
if(_str != s.s_str)
{
if(--GetRefCount() == 0)
{
delete[] (_str-4);
}
_str = s.s_str;
GetRefCount()++;
}
return *this;
}
int& GetRefCount()
{
return (*(int *)(_str-4));
}