淺拷貝會造成指針懸掛的問題。
舉個例子:兩個對象是s1和s2的指針_str都指向new開辟的同一塊空間,如下圖,主程序結束時,對象逐個撤銷,先撤銷對象s2,會調用析構函數釋放動態分配的內存;再撤銷對象s1時,s1._str所指向的內存空間已經是無法訪問了,而s2._str原先指向的那塊內存卻無法釋放,出現了所謂的指針懸掛! 兩個對象企圖釋放同一塊內存,從而導致一塊內存被釋放兩次這也是不行的,運行會出錯。
#include <iostream>
using namespace std;
class String
{
public:
String(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()
{
delete[] _str;
}
private:
char* _str;
};
void Test()
{
String s1("Lynn" );
String s2=s1;
}
int main()
{
Test();
system("pause" );
return 0;
}
深拷貝 深拷貝解決了指針懸掛的問題,當調用拷貝構造或賦值運算符的重載函數時,程序會生成一份該內存的拷貝,這樣每個指針都會指向一塊相對獨立的空間,撤銷對象時調用析構函數,分別釋放他們自己的動態分配的內存,相互之間不影響。如下圖:
深拷貝
///////////////////////////////////////////////////////////////////////////////////////
// 寫String類的構造函數時一定要注意參數問題
// 首先要考慮到構造的對象分有參數和無參數兩種情況
// 構造對象的時候不能直接賦值,否則一塊內存兩次釋放的話程序會出錯
// 無參的構造函數不能將_str指針賦值為NULL,因為不能strlen(NULL)
// 賦值運算符的重載要考慮到有可能分配內存失敗的問題
// 當然,記得要給'\0'分配空間哦
// By:Lynn-Zhang
//////////////////////////*****************////////////////////////////////////////////
#include<iostream>
using namespace std;
class String
{
public:
String(char * str="") //不能strlen(NULL)
:_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 )
{
/* //有可能開辟空間失敗,但是卻破壞了_str的內容
delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str); */
char* tmp = new char [strlen(s ._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
swap(_str, tmp);
}
return *this ;
}
char* CStr()
{
return _str;
}
~String()
{
delete[] _str;
}
private:
char* _str;
};
//函數測試
void Test()
{
String s1("aaaaa" );
cout << s1.CStr() << endl;
String s2(s1);
cout << s2.CStr() << endl;
String s3 = s1;
s3= s2;
cout << s3.CStr() << endl;
String s4;
// s4 = s1;
cout << s4.CStr() << endl;
}
int main()
{
Test();
system("pause" );
return 0;
}