C++中的深拷貝與淺拷貝
阿新 • • 發佈:2018-12-19
淺拷貝問題丟擲:
#define _CRT_SECURE_NO_WARNINGS #include "iostream" using namespace std; /* 淺拷貝問題丟擲 */ class ShallowCopy { public: ShallowCopy(const char * myp) { len = strlen(myp); p = (char *)malloc(len + 1); //指標指向誰,就把誰的地址給指標。把記憶體空間首地址給p,+1是\0,沒有的話會記憶體越界。 strcpy(p, myp);//把myp指標所指向的記憶體空間的資料copy到p所指向的記憶體空間。 } ~ShallowCopy() { if ( p != NULL ) { free(p); p = NULL; len = 0; } } private: char * p; int len; }; void ObjPlaymain() { ShallowCopy obj1("abcdefg"); /* 呼叫obj2類的copy建構函式,沒寫,呼叫c++編譯器提供的copy建構函式(淺拷貝) c++編譯器偷懶,淺拷貝,把obj1的屬性拷貝到obj2,只把指標變數的值拷貝過來了,沒有另外開闢記憶體。 */ ShallowCopy obj2 = obj1; } void main() { ObjPlaymain(); system("pause"); }
這樣程式會出現coredump, 可以用如下記憶體圖理解:
c++編譯器預設的copy建構函式,淺拷貝,把obj1的屬性拷貝到obj2,只把指標變數的值拷貝過來了,沒有另外開闢記憶體。這樣obj1和obj2的p指標指向同一塊記憶體空間。
析構的時候,先析構obj2,把那塊記憶體空間析構掉了,同時把記憶體空間置成NULL,把len置成0。然後析構obj1,此時那塊記憶體空間是垃圾值(已經析構過了),則此時的obj1的指標p是個野指標,呼叫obj1的解構函式,同一塊記憶體空間被析構了2次。析構的時候程式會出現coredump。
那怎麼辦呢?
藍色箭頭是我們的解決方案。既然c++編譯器提供的copy建構函式不行,我們就手動編寫copy建構函式唄,讓2個指標不要指向同一塊記憶體,給它重新開闢。所以必要的時候,必須手工編寫copy建構函式。
下面是解決方案:
#define _CRT_SECURE_NO_WARNINGS #include "iostream" using namespace std; /* 淺拷貝問題解決:不使用c++編譯器提供的copy建構函式,手工編寫copy建構函式,使用深拷貝。 */ class DeepCopy { public: DeepCopy(const char * myp) { len = strlen(myp); p = (char *)malloc(len + 1 ); strcpy(p, myp); } DeepCopy(const DeepCopy & obj1) { len = strlen(obj1.p); p = (char *)malloc(len + 1); strcpy(p, obj1.p); } ~DeepCopy() { if (p != NULL) { free(p); p = NULL; len = 0; } } private: char * p; int len; }; void ObjPlaymain() { DeepCopy obj1("abcdefg"); DeepCopy obj2 = obj1; } void main() { ObjPlaymain(); system("pause"); }
這樣就不會出現問題了,ok!
之前說過初始化和賦值不一樣,如果用=連線obj1和obj2,又會怎樣呢?
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;
/*
=也不行,需要過載操作符
*/
class DeepCopy
{
public:
DeepCopy(const char * myp)
{
len = strlen(myp);
p = (char *)malloc(len + 1);
strcpy(p, myp);
}
~DeepCopy()
{
if (p != NULL)
{
free(p);
p = NULL;
len = 0;
}
}
private:
char * p;
int len;
};
void ObjPlaymain()
{
DeepCopy obj1("abcdefg");
DeepCopy obj2("obj2");
/*
=操作,把obj1的屬性copy給obj2,用c++預設的copy建構函式,
和剛才淺拷貝一樣,析構時出現問題,需要過載操作符=
*/
obj2 = obj1;
}
void main()
{
ObjPlaymain();
system("pause");
}
結果發現,和剛才一樣,也會出現同樣的問題。
依舊用記憶體圖解釋:
人家obj1和obj2本來指向不同的記憶體空間,各不干擾。結果你"obj2=obj1"一下,把obj1的值賦值給obj2,obj2的p和len和obj1一模一樣,把人家obj2的p指標活生生指到obj1那塊記憶體空間了,結果造成obj1和obj2的指標又指向同一塊記憶體空間了,析構的時候又出現coredump。而且,原來人家obj2的0xbb11那塊記憶體被暴露出來了,記憶體洩露了。
這個"="也太膚淺了吧,看來需要操作符過載嘍!