c與c++動態內存管理
首先我們來看看它們都是怎麼使用的吧。
malloc
void *malloc( size_t size );
calloc
//num:開闢的這塊空間有多少個元素
//size:每個元素的字節數
void *calloc( size_t num, size_t size );
realloc
//memblock需要擴容的指針
//size 需要擴充到n個字節
void *realloc( void *memblock, size_t size );
new
new + 需要申請的類型
delete
delete + 指向需要銷燬的那塊空間的指針
new []
//申請一塊內存,用於存放數組
new + 類型[大小]
delete
delete[] + 指針
在C語言中是使用malloc來申請空間,用free來釋放空間
c語言中不止是有malloc還有calloc,realloc,它們都是c語言中用來動態開闢內存的。
那麼他們之間有什麼區別呢?
- malloc:用來申請一塊空間,malloc(需要開開闢的空間的字節數),用malloc開闢的空間,沒有初始化,就只是一塊空間。
- calloc: 開闢一塊空間,並且對這段空間進行初始化。自動將這段空間都初始化爲零。
- realloc:在原來的空間上進行擴容。若是當前內存有足夠的空間可以開闢,那麼就直接在當前內存的後面開闢,若是內存不足,則需要再重新找一塊內存,開闢空間,將之前的值再拷貝過來。然後釋放之前的內存空間。
如果申請成功,返回的都是當前內存的地址,若是申請失敗,則返回NULL。
在不使用這段內存空間後,一定要記得釋放這段空間,用free(指向這段空間的指針)。如果不釋放,就會造成內存泄漏。表面看着沒有什麼影響,但是會一直佔着內存,直到程序結束。
c++與c語言一樣,也有動態內存開闢的機制,支持程序員自己申請與釋放內存。
在c++中就是使用new和delete,new是用來申請空間,delete用來釋放空間。
既然在c語言中已經有了動態開闢內存的機制,爲什麼在c++中還要引入new和delete呢?
下面我們就來說一說malloc/free與new/delete的區別
- malloc/free是c/c++中的函數,而new/delete則是操作符。從它們的使用方式就可以看出來。malloc/free使用時需要有參數,參數必須得有括號。而new/delete則不需要用括號,後面直接跟需要創建的類型就可以。
- malloc/free只是負責開闢空間與釋放空間,new與delete則是還需要調用構造函數與析構函數。對開闢的空間要進行初始化,若是要銷燬,就需要調用析構函數,來清理空間。
- malloc/free需要自己提供需要開闢的字節數,並且返回值爲void*,這就需要程序員對接收這塊空間的變量類型確定,並且要強轉返回的類型爲想要的類型,不然就會出錯。但是new/delete只需要提供想要開闢的類型即可。而且返回的就是對應類型的指針。不需要再手動強轉。
他們的相同點就是都可以申請空間/釋放空間。
接下來,我們詳細的說明一下它們的用法
new / delete
它們是動態管理對象的。調用構造析構函數。
現在舉個例子來驗證一下:
#include<iostream>
using namespace std;
class AA
{
public:
AA()
{
cout<<"AA()"<<endl;
}
~AA()
{
cout<<"~AA()"<<endl;
}
};
int main()
{
AA *p1 = new AA;
delete p1;
return 0;
}
從上面的這個例子能看出,new/delete在對自定義類型使用的時候,,會自動的調用它們的構造函數與析構函數。創建一個對象就會調用一次構造函數。銷燬一個對象的時候也會調用一次其析構函數。那麼new[]/delete[]是怎麼調用構造析構函數的呢?
AA *p1 = new AA[3];
delete[] p1;
上面這段程序,創建了一個數組,這個數組時候一個存對象的數組,有三個元素。所以需要對每一個對象調用構造函數,並且清理的時候也需要調用三次析構函數。就出現了上圖的結果。
上面介紹的就是new/delete,new[]/delete[]的簡單用法。接下來說一說它們的匹配使用問題。
new/delete是一組,new[]/delete[]是一組。一定是要匹配使用,不可以打亂組合。否則可能會出現內存泄漏甚至程序崩潰。從上面說到的來看,new/delete都是隻調用一次,new[]/delete[]調用幾次取決於數組中的元素個數。所以,如果出現了不匹配使用,那麼調用構造函數與析構函數的次數也就不會匹配,那麼就會導致程序出問題。這就是不匹配使用的爲什麼會導致錯誤的原因。
在c++中還有別的動態內存管理的接口。
- operator new(size_t size)
- operator delete(size_t size)
- operator new[](size_t size)
- operator delete[](size_t size)
operator new/operator delete是一組,匹配使用,負責開闢空間與釋放空間。它們的作用相當與c語言中的malloc與free,與它們的用法一樣,實際上,operator new/operator delete是malloc/free的一層封裝。
operator new[]/operator delete[]是一組,匹配使用,負責開闢連續的數組空間與釋放連續的空間。
這四個接口都是函數,不是操作符。看樣子是有點像new/delete的運算符重載,其實並不是。new/delete,new[]/delete[]是通過這四個對應的函數來獲取內存與釋放內存。它們不會調用對象的構造函數與析構函數來初始化對象與清理對象。
具體過程如下所示:
調用構造函數是new操作符完成的,new又調用operator new開闢內存。而在operator new中有個malloc函數,是負責開闢空間。所以c++中的內存管理就是在c語言的基礎上進行了封裝與修改得到了c++中的內存管理機制。
new[n]是開闢一段連續的空間。這段連續的空間裏面存放的是相同的類型數據。new負責調用n次構造函數再調用operator new[],來開闢空間。與new一樣,底層實現還是malloc函數。
delete與new類似,delete操作符負責調用一次析構函數,再調用一次operator delete函數,operator delete負責清理空間,operator delete函數是free函數的封裝,所以,其實是free釋放的空間。
delete[]這個操作符就有些不一樣了。因爲delete[]不需要寫具體數字,那麼,我們怎麼知道需要調用幾次析構函數呢?
其實在new[]時,在開闢的空間前四個字節,存儲的是n。即當前這個數組中元素的個數。在需要delete這個數組時,只需向前讀四個字節,就可以知道當前數組的個數,即知道需要調用幾次析構函數。