在C++中也是少不了對內存的管理,在C++中只要有new的地方,在寫代碼的時候都要想著delete。 new分配的時堆內存,在函數結束的時候不會自動釋放,如果不delete我分配的堆內存,則會造成內存洩露。所以我們要學會內存管理,不要內存洩露。在C++中的內存管理機制和OC中的還不太一樣,在OC中的ARC機制會給程序員的內存管理省不少事,但在C++中沒有ARC所以我們要自己管理好自己開辟的內存。Java中也有自己相應的內存管理機制,比如JDBC裡的獲取的各種資源在finally裡進行close等
那麼什麼情況下我們寫的程序會出現內存洩露呢?下面我們將會舉一個簡單的例子來分析一下C++中的內存管理機制。
1.建立一個測試類TestClass, TestClass類中有一個私有的屬性(指針類型),有一個無慘的構造函數,有一個析構函數,還有一個display方法用於輸出對象的信息
測試類的聲明如下:
1 2 3 4 5 6 7 8 9 10 11 12 13//內存管理的測試類
class
TestClass
{
private
:
char
*name;
public
:
//無參構造函數
TestClass();
//析構函數
~TestClass();
//描述方法
void
display();
};
在xxx.cpp文件中定義類的實現方法
實現構造方法,實現構造方法時要給屬性指針分配空間,不然的話屬性指針沒有分配內存地址,調用時程序會崩潰,用new方法進行堆分配
1 2 3 4 5 6 7 8 9//無參構造函數
TestClass::TestClass()
{
cout <<
"TestClass()"
<<endl;
//給指針屬性分配內存(堆分配)
this
->name =
new
char
[255];
//進行初始化
strcpy
(
this
->name,
"ludashi"
);
}
實現析構函數在析構函數中要對構造函數中堆分配的內存進行delete,不然會造成內存洩露
1 2 3 4 5 6//析構函數
TestClass::~TestClass()
{
delete
[]
this
->name;
cout <<
"~TestClass()"
<< endl;
}
實現display函數,進行name的打印測試
1 2 3 4 5//描述方法
void
TestClass::display()
{
cout <<
this
->name <<endl;
}
2. 在main函數中進行測試
實例化對象時進行堆分配:需要手動進行內存的釋放,不然也會造成內存的洩露
1 2//TestClass類的初始化,堆分配,需要delete
TestClass * testClass =
new
TestClass();
實例化對象的棧分配:不需要手動釋放內存,大括號結束時就自動釋放棧內存
1 2//棧分配,不用delete,出大括號後自動釋放
TestClass stackClass = TestClass()
信息的打印輸出
1testClass->display();
調用delete來釋放堆分配的對象
1delete
testClass;
3.程序運行結果:如果不加delete testClass; 析構函數只會調用一個,因為堆分配的對象不會自動釋放,需要手動釋放,不加則會造成內存的洩露
1 2 3 4 5TestClass()
TestClass()
ludashi
~TestClass()
~TestClass()
4.拷貝構造函數
如果在main函數中加入下面這句話,程序在運行時就會崩掉,如果要想程序正常運行可以把析構函數中的delete [] this->name;注釋掉就可以運行。不過這樣會引起內存的洩露。那麼我們來研究一下為什麼加上下面這句話程序會崩掉呢?原因是下那句話的意思是copyTest和stackClass指向同一塊棧內存,當其中一個調用析構函數時就會把name給delete掉,另一個在析構調用delete時就會報錯。怎麼從基本上解決問題呢?接下來就是拷貝構造函數出場的時候啦。
1TestClass copyTest = stackClass;
下面是拷貝構造函數的定義方法
1 2 3 4 5 6 7 8//拷貝構造函數
TestClass::TestClass(
const
TestClass &test)
{
//在堆中分配新的內存
this
->name =
new
char
[255];
//進行拷貝
strcpy
(
this
->name, test.name);
}
在main函數中調用拷貝構造函數 ,這樣的代碼有不會有剛才的問題了
1TestClass copyTest = stackClass;
5.再提內存管理,不禁又想到初學C++那會的一句話“先構造的後析構”;有new的地方就得想著delete,為了避免內存洩露。
上面的拷貝構造函數的作用是在聲明對象的時候可以利用拷貝構造函數給新的對象賦值,如果像下面的這種情況就會出現過度釋放的問題;
1 2 3TestClass test1 = TestClass();
TestClass test2 = TestClass();
test2 = test1;
接下來就該操作符重載出場的時候了(operator = )把=號進行重載
1 2 3 4 5 6//對象之間的賦值:操作符重載
TestClass & TestClass :: operator = (
const
TestClass &test)
{
strcpy
(
this
->name, test.name);
return
*
this
;
}