C++之深、浅拷贝

时间:2021-12-19 19:49:25

    先给出一段程序:

void fun()
{
int *pTest1 = new int[10];
int *pTest2 = pTest1;
delete[] pTest1;
delete[] pTest2;
}
int main()
{
fun();
return 0;
}

      以上程序运行会崩溃,由于简单的“ = ”只能将pTest1和pTest2指向同一块空间,没有给pTest2开辟新的空间,所以,在delete对象时,该空间被释放了两次,因此,程序崩溃。

    在类中,更容易出现这种情况,例如以下程序:

class String
{
public:
String(const char * pData = "")
: _pData(new char[strlen(pData) + 1])
{
strcpy(_pData, pData);
}
String(const String& _string)
{
if (this != &_string)
{
_pData = _string._pData;
}
}
~String()
{
if (NULL != _pData)
{
delete[] _pData;
_pData = NULL;
}
}
private:
char *_pData;
};
void FunTest()
{
String s1;
String s2("hallo world");
String s3(s2);
}
int main()
{
FunTest();
}
    程序运行后,会出现崩溃。分析FunTest函数:

    String s1:调用构造函数,由于没有参数,在构造函数内有缺省参数,所以,将S1的_pData赋为空。

    String s2("hallo world"):调用构造函数,给_pData分配空间,将形参的内容拷贝到_pData中。

    String s3(s2):调用拷贝构造函数,将s3的_pData指向形参的_pData,即s3._pData指向s2._pData。所以,两个指向的是同一块空间。

    当程序继续往下执行时,局部变量s3被析构,调用析构函数,将s3._pData所指向的空间释放,并将s3._pData赋为NULL。当s2被析构,调用析构函数,因为s2._pData一直指向申请空间,s2._pData不为空,现在又要去释放已经被s3._pData释放的空间,所以会出现问题。

    当类里面有指针对象时,拷贝构造和赋值运算符重载只进行值拷贝(浅拷贝),两个对象指向同一块内存,对象销毁时该空间被释放了两次,因此程序崩溃!

        String s3(s2);
String s4=s3;

    浅拷贝:编译器只是直接将指针的值拷贝过来,结果多个对象共用同一块内存,当一个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,所以在对这段内存进行操作的时候,发生了访问违规。

    要想解决这一问题,就得进行深拷贝,也就是在拷贝构造函数和赋值运算符重载的实现时,申请新的空间,并把要拷贝的对象的值复制一份放入新开辟的空间中,例如以下程序:

class String
{
public:
char* _pstr;
public:
String(char* pstr="")
{
if (pstr == NULL)
{
_pstr = new char[1];
*_pstr = '\0';
}
else
{
_pstr = new char[strlen(pstr) + 1];
strcpy(_pstr,pstr);
}
}
String(const String&s)
{
_pstr = new char[strlen(s._pstr) + 1];
strcpy(_pstr, s._pstr);
}
String& operator = (const String& s)
{
if (this != &s)
{
char *pstr = new char[strlen(s._pstr) + 1];
strcpy(pstr, s._pstr);
delete[] _pstr;
_pstr = pstr;
}
return *this;
}
};<pre name="code" class="cpp">int main()
{
String s1("hallo world");
cout << s1._pstr << endl;
String s2(s1);
cout << s2._pstr << endl;
String s3 = s2;
cout << s3._pstr << endl;
return 0;
}

 

    在这个程序之中,拷贝构造函数和赋值运算符重载都分配了新的空间,并拷贝了值,这样使得在对象使用完成后进行析构时,不会多次析构一个空间而出现错误,s1、s2、s3各都指向自己的数据块,析构时,释放各自的空间。这也就是深拷贝。