深拷貝拯救指標重複釋放(淺拷貝)造成的記憶體洩漏
阿新 • • 發佈:2019-01-10
1. 淺拷貝以及記憶體洩漏的背景
先考慮一種情況,對一個已知物件進行拷貝,編譯系統會自動呼叫一種建構函式——拷貝建構函式,如果使用者未定義拷貝建構函式,則會呼叫預設拷貝建構函式。
#include <iostream>
#include "student.h"
int main()
{
Student s1;
Student s2(s1);//Student s2 = s1;//複製物件
return 0;
}
//仔細看,物件中存在一個指標成員,這個是我們研究的核心 #ifndef STUDENT_H #define STUDENT_H class Student { private: int num; char *name; public: Student(); ~Student(); }; #endif
// 注意看解構函式,我們對物件的指標成員進行記憶體的釋放 #include "student.h" #include <iostream> using namespace std; Student::Student() { name = new char(20); cout << "Student" << endl; } Student::~Student() { cout << "~Student " << (int)name << endl; delete name; name = NULL; }
執行結果:呼叫一次建構函式,呼叫兩次解構函式,兩個物件的指標成員所指記憶體相同,這會導致什麼問題呢?
name指標被分配一次記憶體,但是程式結束時該記憶體卻被釋放了兩次,會造成記憶體洩漏問題!
這是由於編譯系統在我們沒有自己定義拷貝建構函式時,會在拷貝物件時呼叫預設拷貝建構函式,進行的是淺拷貝!即對指標name拷貝後會出現兩個指標指向同一個記憶體空間。
所以,在對含有指標成員的物件進行拷貝時,必須要自己定義拷貝建構函式,使拷貝後的物件指標成員有自己的記憶體空間,即進行深拷貝,這樣就避免了記憶體洩漏發生。
2. 深拷貝進行記憶體洩漏進行拯救
//student.h #ifndef STUDENT_H #define STUDENT_H class Student { private: int num; char *name; public: Student();//建構函式 ~Student();//解構函式 Student(const Student &s); //重要:拷貝建構函式,const防止物件被改變 }; #endif
//student.cpp
#include "student.h"
#include <iostream>
#include <string.h>
using namespace std;
Student::Student()
{
name = new char(20);
cout << "Student " << endl;
}
Student::~Student()
{
cout << "~Student " << (int)name << endl;
delete name;
name = NULL;
}
Student::Student(const Student &s)
{
name = new char(20); // 對於每一次物件的引用,我們都重新開闢記憶體空間
memcpy(name, s.name, strlen(s.name));
cout << "copy Student " << endl;
}
執行結果:呼叫一次建構函式,一次自定義拷貝建構函式,兩次解構函式。兩個物件的指標成員所指記憶體不同。
總結:淺拷貝只是對指標的拷貝,拷貝後兩個指標指向同一個記憶體空間,深拷貝不但對指標進行拷貝,而且對指標指向的內容進行拷貝,經深拷貝後的指標是指向兩個不同地址的指標。
再說幾句:
當物件中存在指標成員時,除了在複製物件時需要考慮自定義拷貝建構函式,還應該考慮以下兩種情形:
1.當函式的引數為物件時,實參傳遞給形參的實際上是實參的一個拷貝物件,系統自動通過拷貝建構函式實現;
2.當函式的返回值為一個物件時,該物件實際上是函式內物件的一個拷貝,用於返回函式呼叫處。
3. 參考文獻
《C++ primer》