在拷贝构造一个对象时,我们可能会用到空间操作,这时就会出现浅拷贝和深拷贝的问题。
浅拷贝:把目标空间的地址拷贝过去,多个对象共用一块空间。这么做节省空间,但是如果我们不做写时拷贝,当修改一个对象所管理的空间时,其他对象也会被修改。
下面这段代码是一个字符串类的浅拷贝:
class String
{
public:
String(const char* pStr = "")
{
_pStr = new char;
*_pStr = *pStr;
}
String(const String& s)
:_pStr(s._pStr)
{}
String& operator=(const String& s)
{
_pStr = s._pStr;
return *this;
}
~String()
{
delete[] _pStr;
}
private:
char* _pStr;
};
在上面的代码里再加一点东西,在空间顶部多开辟4个字节,用于存放计数器,计数器记录管理目标空间的对象的个数:
class String
{
public:
String(const char* pStr = "")
{
_pStr = new char[4+1];
*(int*)_pStr = 0;
_pStr = _pStr + 4;
*_pStr = *pStr;
}
String(const String& s)
{
char *_pStr = new char[strlen(s._pStr) + 1 + 4 ];
(*(int*)_pStr)++;
_pStr = _pStr + 4;
_pStr = s._pStr;
}
String& operator=(const String& s)
{
char *ptem = new char[strlen(s._pStr) + 1] + 4;
(*(int*)ptem)++;
ptem = ptem + 4;
_pStr = s._pStr;
return *this;
}
~String()
{
*(int*)_pStr--;
if (*(int*)_pStr == 0)
delete[] _pStr;
}
private:
char* _pStr;
};
深拷贝:把目标空间的内容复制一份,再拷贝过去,每个对象管理自己独立的一块空间,相互之间不会影响。
下面是深拷贝的代码:
class String
{
public:
String(const char* pStr = "")
{
_pStr = new char;
*_pStr = *pStr;
}
String(const String& s)
{
char *_pStr = new char[strlen(s._pStr) + 1];
int i = 0;
for (i = 0; i < strlen(s._pStr); i++)
{
_pStr[i] = s._pStr[i];
}
_pStr[i + 1] = '\0';
}
String& operator=(const String& s)
{
char *ptem = new char[strlen(s._pStr) + 1];
int i = 0;
for (i = 0; i < strlen(s._pStr); i++)
{
ptem[i] = s._pStr[i];
}
_pStr[i + 1] = '\0';
swap(ptem, _pStr);
delete[] ptem;
return *this;
}
~String()
{
delete[] _pStr;
}
private:
char* _pStr;
};
写时拷贝:它像浅拷贝一样,但是在一个对象更改空间内容时,它会像深拷贝一样把空间内容复制一份,这样每次更改就不会影响其他对象的内容。
具体代码:
class String
{
public:
//不涉及内容更改时,它和浅拷贝一样
String(const char* pStr = "")
{
_pStr = new char[4+1];
*(int*)_pStr = 0;
_pStr = _pStr + 4;
*_pStr = *pStr;
}
String(const String& s)
{
char *_pStr = new char[strlen(s._pStr) + 1 + 4 ];
(*(int*)_pStr)++;
_pStr = _pStr + 4;
_pStr = s._pStr;
}
String& operator=(const String& s)
{
char *ptem = new char[strlen(s._pStr) + 1] + 4;
(*(int*)ptem)++;
ptem = ptem + 4;
_pStr = s._pStr;
return *this;
}
~String()
{
*(int*)_pStr--;
if (*(int*)_pStr == 0)
delete[] _pStr;
}
//涉及内容更改时,使用写时拷贝
void reString(int i, char c)
{
if (*(int*)_pStr == 1)
{
_pStr = _pStr + 4;
_pStr[i] = c;
return;
}
(*(int*)_pStr)--;
char *ptem = new char[strlen(_pStr) + 1 + 4 ];
int i = 0;
for (i = 0; i < strlen(_pStr); i++)
{
ptem[i] = _pStr[i];
}
_pStr[i + 1] = '\0';
(*(int*)ptem) = 1;
swap(ptem, _pStr);
delete[] ptem;
}
private:char* _pStr;};