C++ ---釋放內存(new和delete)

                                                                                        C++ ---釋放內存(new和delete)html

C++動態分配和釋放內存 @c.biancheng.net/view/206.htmlios

-------------------------------------------------------------------------------------------------程序員

C語言中,動態分配內存用 malloc() 函數,釋放內存用 free() 函數。以下所示:編程

  1. int *p = (int*) malloc( sizeof(int) * 10 ); //分配10個int型的內存空間
  2. free(p); //釋放內存

C++中,這兩個函數仍然可使用,可是C++又新增了兩個關鍵字,new 和 delete:new 用來動態分配內存,delete 用來釋放內存。

用 new 和 delete 分配內存更加簡單:
數組

  1. int *p = new int; //分配1個int型的內存空間
  2. delete p; //釋放內存

new 操做符會根據後面的數據類型來推斷所需空間的大小。

若是但願分配一組連續的數據,可使用 new[]:
安全

  1. int *p = new int[10]; //分配10個int型的內存空間
  2. delete[] p;

用 new[] 分配的內存須要用 delete[] 釋放,它們是一一對應的。

和 malloc() 同樣,new 也是在堆區分配內存,必須手動釋放,不然只能等到程序運行結束由操做系統回收。爲了不內存泄露,一般 new 和 delete、new[] 和 delete[] 操做符應該成對出現,而且不要和C語言中 malloc()、free() 一塊兒混用。

在C++中,建議使用 new 和 delete 來管理內存,它們可使用C++的一些新特性,最明顯的是能夠自動調用構造函數和析構函數,後續咱們將會講解。
函數

--------------------------------------------------------------------------------------------------------------ui

數組的長度是預先定義好的,在整個程序中固定不變。C++ 不容許定義元素個數不肯定的數組。例如:this

  1. int n;
  2. int a[n]; //這種定義是不容許的

可是在實際的編程中,每每會出現所需的內存空間大小取決於實際要處理的數據多少,而實際要處理的數據數量在編程時沒法肯定的狀況。若是老是定義一個儘量大的數組,又會形成空間浪費。況且,這個「儘量大」到底應該多大才夠呢?

爲了解決上述問題,C++ 提供了一種「動態內存分配」機制,使得程序能夠在運行期間,根據實際須要,要求操做系統臨時分配一片內存空間用於存放數據。此種內存分配是在程序運行中進行的,而不是在編譯時就肯定的,所以稱爲「動態內存分配」。

在 C++ 中,經過 new 運算符來實現動態內存分配。new 運算符的第一種用法以下:spa

T *p = new T;

其中,T 是任意類型名,p 是類型爲 T* 的指針

這樣的語句會動態分配出一片大小爲 sizeof(T) 字節的內存空間,而且將該內存空間的起始地址賦值給 p。例如:

  1. int* p;
  2. p = new int;
  3. *p = 5;

第二行動態分配了一片 4 個字節大小的內存空間,而 p 指向這片空間。經過 p 能夠讀寫該內存空間。

new 運算符還有第二種用法,用來動態分配一個任意大小的數組:

T *p =new T[N];

其中,T 是任意類型名,p 是類型爲 T* 的指針,N 表明「元素個數」,能夠是任何值爲正整數的表達式,表達式中能夠包含變量、函數調用等。這樣的語句動態分配出 N × sizeof(T) 個字節的內存空間,這片空間的起始地址被賦值給 p。例如:

  1. int* pn;
  2. int i = 5 ;
  3. pn = new int[i*20];
  4. pn[0] = 20 ;
  5. pn[100] = 30;

最後一行編譯時沒有問題,但運行時會致使數組越界。由於上面動態分配的數組只有 100 個元素,pn[100] 已經不在動態分配的這片內存區域以內了。

若是要求分配的空間太大,操做系統找不到足夠的內存來知足,那麼動態內存分配就會失敗,此時程序會拋出異常。關於這一點,將在後續章節中介紹。

程序從操做系統動態分配所得的內存空間在使用完後應該釋放,交還操做系統,以便操做系統將這片內存空間分配給其餘程序使用。C++ 提供 delete 運算符,用以釋放動態分配的內存空間。delete 運算符的基本用法以下:

delete p;

p 是指向動態分配的內存的指針。p 必須指向動態分配的內存空間,不然運行時極可能會出錯。例如:

  1. int* p = new int;
  2. *p = 5;
  3. delete p;
  4. delete p; //本句會致使程序出錯

上面的第一條 delete 語句正確地釋放了動態分配的 4 個字節內存空間。第二條 delete 語句會致使程序出錯,由於 p 所指向的空間已經釋放,p 再也不是指向動態分配的內存空間的指針了。

若是是用 new 的第二種用法分配的內存空間,即動態分配了一個數組,那麼釋放該數組時,應以以下形式使用 delete 運算符:

delete[] p;

p 依然是指向動態分配的內存的指針。例如:

  1. int* p = new int[20];
  2. p[0] = 1;
  3. delete[] p;

一樣地,要求被釋放的指針 p 必須是指向動態分配的內存空間的指針,不然會出錯。

若是動態分配了一個數組,可是卻用delete p的方式釋放,沒有用[],則編譯時沒有問題,運行時也通常不會發生錯誤,但實際上會致使動態分配的數組沒有被徹底釋放。

牢記,用 new 運算符動態分配的內存空間,必定要用 delete 運算符釋放。不然,即使程序運行結束,這部份內存空間仍然不會被操做系統收回,從而成爲被白白浪費掉的內存垃圾。這種現象也稱爲「內存泄露」。

若是一個程序不停地進行動態內存分配而老是沒有釋放,那麼可用內存就會被該程序大量消耗,即使該程序結束也不能恢復。這就會致使操做系統運行速度變慢,甚至沒法再啓動新的程序。可是,只要從新啓動計算機,這種狀況就會消失。

編程時若是進行了動態內存分配,那麼必定要確保其後的每一條執行路徑都能釋放它。

另外還要注意,釋放一個指針,並不會使該指針的值變爲 NULL。

---------------------------------------------------------------------------------------

一般定義變量或者對象,編譯器在編譯時均可以根據該變量或對象的類型知道所需內存空間的大小,從而系統在適當的時候爲他們分配肯定的存儲空間,這種內存分配被稱爲靜態存儲分配。

  有些操做對象只有在程序運行時才能肯定,這樣編譯器在編譯時就沒法爲他們預約存儲空間,只能在程序運行時,系統根據運行時的要求進行內存分配,這種方法稱爲動態內存分配。

  全部動態存儲分配都在堆區中進行。

內存的分配與釋放
  當程序運行到須要一個動態分配的變量或對象,必須向系統申請取得堆中的一塊所需大小的存儲空間,用於存儲該變量或對象。當再也不使用該變量或對象時,也就是它生命結束之時,要顯式釋放它所佔用的存儲空間,這樣系統就能對該堆空間進行再分配,作到重複使用有限資源。

  在C++中,申請和釋放堆中分配的存儲空間,分別使用new和delete的兩個運算符來完成,使用格式以下:
  指針變量名 = new 類型名(初始化式)
    delete指針名 
  new運算符返回的是一個指向所分配類型變量(對象)的指針。對所建立的變量或對象,都是經過該指針來間接操做的,而動態建立的對象自己沒有名字。

複製代碼
#include<iostream>
using namespace std; class ST { private: int a; public: ST(int _a=0):a(_a) { this->a = _a; cout<<"Object was built. "<<endl; } ~ST() { cout<<"Object was free. "<<endl; } }; void malloc_free() { ST *tmp = (ST*)malloc(sizeof(ST)); free(tmp); } void new_delete() { ST *tmp = new ST[1];//可是new爲對象數組分配空間不可初始化 delete []tmp; } void main() { ST *pt = new ST(1);//new爲對象分配空間就可初始化 delete [] pt;//delete p 是刪除pt所指空間,而並不是刪除pt,爲了預防野指針可將pt=NUll malloc_free(); new_delete(); }
複製代碼

 運行結果

malloc&free,new&delete都是申請釋放空間,可是,有以下幾點不一樣

1.new申請時不須要強制轉換類型,也不須要申請結束後判斷是否申請到(由於其內部含有未申請到異常退出)

2.new在爲某個對象申請空間時,會調用構造函數,所以可在申請時初始化(對象的構造函數要支持),delete也會在釋放空間時會先調用析構函數

3.由堆區建立對象數組(例如ST*pt = new ST[10]),只能調用缺省的構造函數(缺省構造函數:不含參數的或每一個參數都有默認值的構造函數),不能調用其餘任何構     造函數。若沒有缺省的構造函數則不能建立對象數組。還有,建立對象數組時不能夠初始化。

4.對數組進行動態分配格式以下:
 指針變量名 = new 類型名[下標表達式]

 (下標表達式不是常量表達式,可沒必要再編譯時肯定)
 delete []指向該數組的指針變量名
 這二者必須搭配使用,若delete未加[],編譯器會認爲該指針是指向數組的第一個元素的指針,僅僅回收第一個元素所佔空間。加上[]則會回收整個數組。

@https://www.cnblogs.com/area-h-p/p/10339599.html

-------------------------------------------------------

new 和 delete 是 C++ 用於管理堆內存的兩個運算符,對應於C語言中的 malloc 和 free,可是 malloc 和 free 是函數,而new 和 delete 是運算符。除此以外,new 在申請內存的同時,還會調用對象的構造函數,而 malloc 只會申請內存;一樣,delete 在釋放內存以前,會調用對象的析構函數,而 free 只會釋放內存。C++new運算符申請內存:將調用相應的 operator new(size_t) 函數動態分配內存,在分配到的動態內存塊上 初始化 相應類型的對象(構造函數)並返回其首地址。若是調用構造函數初始化對象時拋出異常,則自動調用 operator delete(void*, void*) 函數釋放已經分配到的內存。delete運算符釋放內存:調用相應類型的析構函數,處理類內部可能涉及的資源釋放,調用相應的 operator delete(void *) 函數。int main(){    T * t = new T(); // 先內存分配,再構造函數    delete t; // 先析構函數,再內存釋放    return 0;}new表達式type  * p_var = new type;   //分配內存,但未初始化int * a = new int; type  * p_var = new type(init);//分配內存時,將 *a 初始化爲 8int * a = new int(8);type *p_var = new type [size]; //分配了3個int大小的連續內存塊,但未初始化int * a = new int[3] ; delete表達式刪除單變量地址空間int *a = new int;delete a;//釋放單個int的空間刪除數組空間int *a = new int[5];delete []a;//釋放int數組空間C內存區域能夠分爲棧,堆,靜態存儲區和常量存儲區。局部變量,函數形參,臨時變量都是在棧上得到內存的,它們獲取的方式都是由編譯器自動執行的。而C標準函數庫提供了許多函數來實現對堆上內存管理,其中包括:malloc函數,free函數,calloc函數和realloc函數。使用這些函數須要包含頭文件stdlib.h。(1)malloc函數malloc函數能夠從堆上得到指定字節的內存空間,其函數聲明以下:void * malloc(int n);其中,形參n爲要求分配的字節數。若是函數執行成功,malloc返回得到內存空間的首地址;若是函數執行失敗,那麼返回值爲NULL。因爲malloc函數值的類型爲void型指針,所以,能夠將其值類型轉換後賦給任意類型指針,這樣就能夠經過操做該類型指針來操做從堆上得到的內存空間。須要注意的是,malloc函數分配獲得的內存空間是未初始化的。所以,通常在使用該內存空間時,要調用另外一個函數memset來將其初始化爲全0。memset函數的聲明以下:void * memset (void * p,int c,int n) ;該函數能夠將指定的內存空間按字節單位置爲指定的字符c。其中,p爲要清零的內存空間的首地址,c爲要設定的值,n爲被操做的內存空間的字節長度。int * p=NULL;p=(int *)malloc(sizeof(int));if(p==NULL){    printf(「Can’t get memory!\n」);}memset(p,0,siezeof(int));(2)free函數從堆上得到的內存空間在程序結束之後,系統不會將其自動釋放,須要程序員來本身管理。一個程序結束時,必須保證全部從堆上得到的內存空間已被安全釋放,不然,會致使內存泄露。void free (void * p);因爲形參爲void指針,free函數能夠接受任意類型的指針實參。可是,free函數只是釋放指針指向的內容,而該指針仍然指向原來指向的地方,此時,指針爲野指針,若是此時操做該指針會致使不可預期的錯誤。安全作法是:在使用free函數釋放指針指向的空間以後,將指針的值置爲NULL。free(p);p=NULL;//注:使用malloc函數分配的堆空間在程序結束以前必須釋放(3)calloc函數calloc函數的功能與malloc函數的功能類似,都是從堆分配內存。其函數聲明以下:void *calloc(int n,int size);函數返回值爲void型指針。若是執行成功,函數從堆上得到size X n的字節空間,並返回該空間的首地址。若是執行失敗,函數返回NULL。該函數與malloc函數的一個顯著不一樣時是,**calloc函數獲得的內存空間是通過初始化的,其內容全爲0。**calloc函數適合爲數組申請空間,能夠將size設置爲數組元素的空間長度,將n設置爲數組的容量。 int * p=NULL;//爲p從堆上分配SIZE個int型空間p=(int *)calloc(SIZE,sizeof(int));if(NULL==p){    printf("Error in calloc.\n");    return -1;}free(p);p = NULL;(4)realloc函數realloc函數的功能比malloc函數和calloc函數的功能更爲豐富,能夠實現內存分配和內存釋放的功能,其函數聲明以下:void * realloc(void * p,int n);其中,指針p必須爲指向堆內存空間的指針,即由malloc函數、calloc函數或realloc函數分配空間的指針。realloc函數將指針p指向的內存塊的大小改變爲n字節。若是n小於或等於p以前指向的空間大小,那麼。保持原有狀態不變。若是n大於原來p以前指向的空間大小,那麼,系統將從新爲p從堆上分配一塊大小爲n的內存空間,同時,將原來指向空間的內容依次複製到新的內存空間上,p以前指向的空間被釋放。relloc函數分配的空間也是未初始化的。 int * p=NULL;p=(int *)malloc(sizeof(int));p=(int *)realloc(p,3*sizeof(int));//釋放p指向的空間realloc(p,0);p=NULL;注:使用malloc函數,calloc函數和realloc函數分配的內存空間都要使用free函數或指針參數爲NULL的realloc函數來釋放。  原文連接:https://blog.csdn.net/dongxianfei/article/details/79031943

相關文章
相關標籤/搜索