自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

看完這篇你還能不懂C語言/C++內(nèi)存管理?

開發(fā) 后端 存儲軟件
C 語言內(nèi)存管理指對系統(tǒng)內(nèi)存的分配、創(chuàng)建、使用這一系列操作。在內(nèi)存管理中,由于是操作系統(tǒng)內(nèi)存,使用不當(dāng)會(huì)造成畢竟麻煩的結(jié)果。本文將從系統(tǒng)內(nèi)存的分配、創(chuàng)建出發(fā),并且使用例子來舉例說明內(nèi)存管理不當(dāng)會(huì)出現(xiàn)的情況及解決辦法。

 C 語言內(nèi)存管理指對系統(tǒng)內(nèi)存的分配、創(chuàng)建、使用這一系列操作。在內(nèi)存管理中,由于是操作系統(tǒng)內(nèi)存,使用不當(dāng)會(huì)造成畢竟麻煩的結(jié)果。本文將從系統(tǒng)內(nèi)存的分配、創(chuàng)建出發(fā),并且使用例子來舉例說明內(nèi)存管理不當(dāng)會(huì)出現(xiàn)的情況及解決辦法。

[[350266]]

一、內(nèi)存

在計(jì)算機(jī)中,每個(gè)應(yīng)用程序之間的內(nèi)存是相互獨(dú)立的,通常情況下應(yīng)用程序 A 并不能訪問應(yīng)用程序 B,當(dāng)然一些特殊技巧可以訪問,但此文并不詳細(xì)進(jìn)行說明。例如在計(jì)算機(jī)中,一個(gè)視頻播放程序與一個(gè)瀏覽器程序,它們的內(nèi)存并不能訪問,每個(gè)程序所擁有的內(nèi)存是分區(qū)進(jìn)行管理的。

在計(jì)算機(jī)系統(tǒng)中,運(yùn)行程序 A 將會(huì)在內(nèi)存中開辟程序 A 的內(nèi)存區(qū)域 1,運(yùn)行程序 B 將會(huì)在內(nèi)存中開辟程序 B 的內(nèi)存區(qū)域 2,內(nèi)存區(qū)域 1 與內(nèi)存區(qū)域 2 之間邏輯分隔。

1.1 內(nèi)存四區(qū)

在程序 A 開辟的內(nèi)存區(qū)域 1 會(huì)被分為幾個(gè)區(qū)域,這就是內(nèi)存四區(qū),內(nèi)存四區(qū)分為棧區(qū)、堆區(qū)、數(shù)據(jù)區(qū)與代碼區(qū)。

棧區(qū)指的是存儲一些臨時(shí)變量的區(qū)域,臨時(shí)變量包括了局部變量、返回值、參數(shù)、返回地址等,當(dāng)這些變量超出了當(dāng)前作用域時(shí)將會(huì)自動(dòng)彈出。該棧的最大存儲是有大小的,該值固定,超過該大小將會(huì)造成棧溢出。

堆區(qū)指的是一個(gè)比較大的內(nèi)存空間,主要用于對動(dòng)態(tài)內(nèi)存的分配;在程序開發(fā)中一般是開發(fā)人員進(jìn)行分配與釋放,若在程序結(jié)束時(shí)都未釋放,系統(tǒng)將會(huì)自動(dòng)進(jìn)行回收。

數(shù)據(jù)區(qū)指的是主要存放全局變量、常量和靜態(tài)變量的區(qū)域,數(shù)據(jù)區(qū)又可以進(jìn)行劃分,分為全局區(qū)與靜態(tài)區(qū)。全局變量與靜態(tài)變量將會(huì)存放至該區(qū)域。

代碼區(qū)就比較好理解了,主要是存儲可執(zhí)行代碼,該區(qū)域的屬性是只讀的。

1.2 使用代碼證實(shí)內(nèi)存四區(qū)的底層結(jié)構(gòu)

由于棧區(qū)與堆區(qū)的底層結(jié)構(gòu)比較直觀的表現(xiàn),在此使用代碼只演示這兩個(gè)概念。首先查看代碼觀察棧區(qū)的內(nèi)存地址分配情況:

  1. #include<stdio.h> 
  2. int main() 
  3.  int a = 0; 
  4.  int b = 0; 
  5.  char c='0'
  6.  printf("變量a的地址是:%d\n變量b的地址是:%d\n變量c的地址是:%d\n", &a, &b, &c); 
  7.  

運(yùn)行結(jié)果為:

我們可以觀察到變量 a 的地址是 2293324 變量 b 的地址是 2293320,由于 int 的數(shù)據(jù)大小為 4 所以兩者之間間隔為 4;再查看變量 c,我們發(fā)現(xiàn)變量 c 的地址為 2293319,與變量 b 的地址 2293324 間隔 1,因?yàn)?c 的數(shù)據(jù)類型為 char,類型大小為 1。在此我們觀察發(fā)現(xiàn),明明我創(chuàng)建變量的時(shí)候順序是 a 到 b 再到 c,為什么它們之間的地址不是增加而是減少呢?那是因?yàn)闂^(qū)的一種數(shù)據(jù)存儲結(jié)構(gòu)為先進(jìn)后出,如圖:

首先棧的頂部為地址的“最小”索引,隨后往下依次增大,但是由于堆棧的特殊存儲結(jié)構(gòu),我們將變量 a 先進(jìn)行存儲,那么它的一個(gè)索引地址將會(huì)是最大的,隨后依次減少;第二次存儲的值是 b,該值的地址索引比 a 小,由于 int 的數(shù)據(jù)大小為 4,所以在 a 地址為 2293324 的基礎(chǔ)上往上減少 4 為 2293320,在存儲 c 的時(shí)候?yàn)?char,大小為 1,則地址為 2293319。由于 a、b、c 三個(gè)變量同屬于一個(gè)棧內(nèi),所以它們地址的索引是連續(xù)性的,那如果我創(chuàng)建一個(gè)靜態(tài)變量將會(huì)如何?在以上內(nèi)容中說明了靜態(tài)變量存儲在靜態(tài)區(qū)內(nèi),我們現(xiàn)在就來證實(shí)一下:

  1. #include<stdio.h> 
  2. int main() 
  3.   
  4.  int a = 0; 
  5.  int b = 0; 
  6.  char c='0'
  7.  static int d = 0; 
  8.   
  9.  printf("變量a的地址是:%d\n變量b的地址是:%d\n變量c的地址是:%d\n", &a, &b, &c); 
  10.   
  11.  printf("靜態(tài)變量d的地址是:%d\n", &d); 
  12.  

運(yùn)行結(jié)果如下:

以上代碼中創(chuàng)建了一個(gè)變量 d,變量 d 為靜態(tài)變量,運(yùn)行代碼后從結(jié)果上得知,靜態(tài)變量 d 的地址與一般變量 a、b、c 的地址并不存在連續(xù),他們兩個(gè)的內(nèi)存地址是分開的。那接下來在此建一個(gè)全局變量,通過上述內(nèi)容得知,全局變量與靜態(tài)變量都應(yīng)該存儲在靜態(tài)區(qū),代碼如下:

  1. #include<stdio.h> 
  2. int e = 0; 
  3. int main() 
  4.   
  5.  int a = 0; 
  6.  int b = 0; 
  7.  char c='0'
  8.  static int d = 0; 
  9.   
  10.  printf("變量a的地址是:%d\n變量b的地址是:%d\n變量c的地址是:%d\n", &a, &b, &c); 
  11.   
  12.  printf("靜態(tài)變量d的地址是:%d\n", &d); 
  13.  printf("全局變量e的地址是:%d\n", &e); 
  14.  

運(yùn)行結(jié)果如下:

從以上運(yùn)行結(jié)果中證實(shí)了上述內(nèi)容的真實(shí)性,并且也得到了一個(gè)知識點(diǎn),棧區(qū)、數(shù)據(jù)區(qū)都是使用棧結(jié)構(gòu)對數(shù)據(jù)進(jìn)行存儲。

在以上內(nèi)容中還說明了一點(diǎn)棧的特性,就是容量具有固定大小,超過最大容量將會(huì)造成棧溢出。查看如下代碼:

  1. #include<stdio.h> 
  2.  
  3. int main() 
  4.  char arr_char[1024*1000000]; 
  5.     arr_char[0] = '0'

以上代碼定義了一個(gè)字符數(shù)組 arr_char,并且設(shè)置了大小為 1024*1000000,設(shè)置該數(shù)據(jù)是方便查看大小;隨后在數(shù)組頭部進(jìn)行賦值。運(yùn)行結(jié)果如下:

這是程序運(yùn)行出錯(cuò),原因是造成了棧的溢出。在平常開發(fā)中若需要大容量的內(nèi)存,需要使用堆。

堆并沒有棧一樣的結(jié)構(gòu),也沒有棧一樣的先進(jìn)后出。需要人為的對內(nèi)存進(jìn)行分配使用。代碼如下:

  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include <malloc.h> 
  4. int main() 
  5.  char *p1 = (char *)malloc(1024*1000000); 
  6.  strcpy(p1, "這里是堆區(qū)"); 
  7.  printf("%s\n", p1); 

以上代碼中使用了strcpy 往手動(dòng)開辟的內(nèi)存空間 p1 中傳數(shù)據(jù)“這里是堆區(qū)”,手動(dòng)開辟空間使用 malloc,傳入申請開辟的空間大小 1024*1000000,在棧中那么大的空間必定會(huì)造成棧溢出,而堆本身就是大容量,則不會(huì)出現(xiàn)該情況。隨后輸出開辟的內(nèi)存中內(nèi)容,運(yùn)行結(jié)果如下:

在此要注意p1是表示開辟的內(nèi)存空間地址。

二、malloc 和 free

在 C 語言(不是 C++)中,malloc 和 free 是系統(tǒng)提供的函數(shù),成對使用,用于從堆中分配和釋放內(nèi)存。malloc 的全稱是 memory allocation 譯為“動(dòng)態(tài)內(nèi)存分配”。

2.1 malloc 和 free 的使用

在開辟堆空間時(shí)我們使用的函數(shù)為 malloc,malloc 在 C 語言中是用于申請內(nèi)存空間,malloc 函數(shù)的原型如下:

  1. void *malloc(size_t size); 

在 malloc 函數(shù)中,size 是表示需要申請的內(nèi)存空間大小,申請成功將會(huì)返回該內(nèi)存空間的地址;申請失敗則會(huì)返回 NULL,并且申請成功也不會(huì)自動(dòng)進(jìn)行初始化。

細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn),該函數(shù)的返回值說明為 void *,在這里 void * 并不指代某一種特定的類型,而是說明該類型不確定,通過接收的指針變量從而進(jìn)行類型的轉(zhuǎn)換。在分配內(nèi)存時(shí)需要注意,即時(shí)在程序關(guān)閉時(shí)系統(tǒng)會(huì)自動(dòng)回收該手動(dòng)申請的內(nèi)存 ,但也要進(jìn)行手動(dòng)的釋放,保證內(nèi)存能夠在不需要時(shí)返回至堆空間,使內(nèi)存能夠合理的分配使用。

釋放空間使用 free 函數(shù),函數(shù)原型如下:

  1. void free(void *ptr); 

free 函數(shù)的返回值為 void,沒有返回值,接收的參數(shù)為使用 malloc 分配的內(nèi)存空間指針。一個(gè)完整的堆內(nèi)存申請與釋放的例子如下:

  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include <malloc.h> 
  4.  
  5. int main() { 
  6.     int n, *p, i; 
  7.     printf("請輸入一個(gè)任意長度的數(shù)字來分配空間:"); 
  8.     scanf("%d", &n); 
  9.      
  10.     p = (int *)malloc(n * sizeof(int)); 
  11.  if(p==NULL){ 
  12.   printf("申請失敗\n"); 
  13.   return 0; 
  14.  }else
  15.   printf("申請成功\n"); 
  16.  }  
  17.   
  18.  memset(p, 0, n * sizeof(int));//填充0  
  19.   
  20.  //查看  
  21.     for (i = 0; i < n; i++) 
  22.         printf("%d ", p[i]); 
  23.     printf("\n"); 
  24.  
  25.     free(p); 
  26.     p = NULL
  27.     return 0; 

以上代碼中使用了 malloc 創(chuàng)建了一個(gè)由用戶輸入創(chuàng)建指定大小的內(nèi)存,判斷了內(nèi)存地址是否創(chuàng)建成功,且使用了 memset 函數(shù)對該內(nèi)存空間進(jìn)行了填充值,隨后使用 for 循環(huán)進(jìn)行了查看。最后使用了 free 釋放了內(nèi)存,并且將 p 賦值 NULL,這點(diǎn)需要主要,不能使指針指向未知的地址,要置于 NULL;否則在之后的開發(fā)者會(huì)誤以為是個(gè)正常的指針,就有可能再通過指針去訪問一些操作,但是在這時(shí)該指針已經(jīng)無用,指向的內(nèi)存也不知此時(shí)被如何使用,這時(shí)若出現(xiàn)意外將會(huì)造成無法預(yù)估的后果,甚至導(dǎo)致系統(tǒng)崩潰,在 malloc 的使用中更需要需要。

2.2 內(nèi)存泄漏與安全使用實(shí)例與講解

內(nèi)存泄漏是指在動(dòng)態(tài)分配的內(nèi)存中,并沒有釋放內(nèi)存或者一些原因造成了內(nèi)存無法釋放,輕度則造成系統(tǒng)的內(nèi)存資源浪費(fèi),嚴(yán)重的導(dǎo)致整個(gè)系統(tǒng)崩潰等情況的發(fā)生。

[[350273]]

內(nèi)存泄漏通常比較隱蔽,且少量的內(nèi)存泄漏發(fā)生不一定會(huì)發(fā)生無法承受的后果,但由于該錯(cuò)誤的積累將會(huì)造成整體系統(tǒng)的性能下降或系統(tǒng)崩潰。特別是在較為大型的系統(tǒng)中,如何有效的防止內(nèi)存泄漏等問題的出現(xiàn)變得尤為重要。例如一些長時(shí)間的程序,若在運(yùn)行之初有少量的內(nèi)存泄漏的問題產(chǎn)生可能并未呈現(xiàn),但隨著運(yùn)行時(shí)間的增長、系統(tǒng)業(yè)務(wù)處理的增加將會(huì)累積出現(xiàn)內(nèi)存泄漏這種情況;這時(shí)極大的會(huì)造成不可預(yù)知的后果,如整個(gè)系統(tǒng)的崩潰,造成的損失將會(huì)難以承受。由此防止內(nèi)存泄漏對于底層開發(fā)人員來說尤為重要。

C 程序員在開發(fā)過程中,不可避免的面對內(nèi)存操作的問題,特別是頻繁的申請動(dòng)態(tài)內(nèi)存時(shí)會(huì)及其容易造成內(nèi)存泄漏事故的發(fā)生。如申請了一塊內(nèi)存空間后,未初始化便讀其中的內(nèi)容、間接申請動(dòng)態(tài)內(nèi)存但并沒有進(jìn)行釋放、釋放完一塊動(dòng)態(tài)申請的內(nèi)存后繼續(xù)引用該內(nèi)存內(nèi)容;如上所述這種問題都是出現(xiàn)內(nèi)存泄漏的原因,往往這些原因由于過于隱蔽在測試時(shí)不一定會(huì)完全清楚,將會(huì)導(dǎo)致在項(xiàng)目上線后的長時(shí)間運(yùn)行下,導(dǎo)致災(zāi)難性的后果發(fā)生。

如下是一個(gè)在子函數(shù)中進(jìn)行了內(nèi)存空間的申請,但是并未對其進(jìn)行釋放:

  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include <malloc.h> 
  4. void m() {  
  5.  char *p1;  
  6.  p1 = malloc(100);  
  7.  printf("開始對內(nèi)存進(jìn)行泄漏..."); 
  8.   
  9. int main() { 
  10.     m(); 
  11.     return 0; 

如上代碼中,使用 malloc 申請了 100 個(gè)單位的內(nèi)存空間后,并沒有進(jìn)行釋放。假設(shè)該 m 函數(shù)在當(dāng)前系統(tǒng)中調(diào)用頻繁,那將會(huì)每次使用都將會(huì)造成 100 個(gè)單位的內(nèi)存空間不會(huì)釋放,久而久之就會(huì)造成嚴(yán)重的后果。理應(yīng)在 p1 使用完畢后添加 free 進(jìn)行釋放:

  1. free(p1); 

以下示范一個(gè)讀取文件時(shí)不規(guī)范的操作:

  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include <malloc.h> 
  4. int m(char *filename) {  
  5.  FILE* f; 
  6.  int key;  
  7.  f = fopen(filename, "r");  
  8.  fscanf(f, "%d", &key);  
  9.  return key;  
  10.   
  11. int main() { 
  12.     m("number.txt"); 
  13.     return 0; 

以上文件在讀取時(shí)并沒有進(jìn)行 fclose,這時(shí)將會(huì)產(chǎn)生多余的內(nèi)存,可能一次還好,多次會(huì)增加成倍的內(nèi)存,可以使用循環(huán)進(jìn)行調(diào)用,之后在任務(wù)管理器中可查看該程序運(yùn)行時(shí)所占的內(nèi)存大小,代碼為:

  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include <malloc.h> 
  4. int m(char *filename) {  
  5.  FILE* f; 
  6.  int key;  
  7.  f = fopen(filename, "r");  
  8.  fscanf(f, "%d", &key);  
  9.  return key;  
  10.   
  11. int main() { 
  12.  int i; 
  13.  for(i=0;i<500;i++) { 
  14.      m("number.txt"); 
  15.  } 
  16.     return 0; 

可查看添加循環(huán)后的程序與添加循環(huán)前的程序做內(nèi)存占用的對比,就可以發(fā)現(xiàn)兩者之間添加了循環(huán)的代碼將會(huì)成本增加占用容量。

未被初始化的指針也會(huì)有可能造成內(nèi)存泄漏的情況,因?yàn)橹羔樜闯跏蓟赶虿豢煽?,如?/p>

  1. int *p; 
  2. *p = val; 

包括錯(cuò)誤的釋放內(nèi)存空間:

  1. pp=p; 
  2. free(p);  
  3. free(pp); 

釋放后使用,產(chǎn)生懸空指針。在申請了動(dòng)態(tài)內(nèi)存后,使用指針指向了該內(nèi)存,使用完畢后我們通過 free 函數(shù)釋放了申請的內(nèi)存,該內(nèi)存將會(huì)允許其它程序進(jìn)行申請;但是我們使用過后的動(dòng)態(tài)內(nèi)存指針依舊指向著該地址,假設(shè)其它程序下一秒申請了該區(qū)域內(nèi)的內(nèi)存地址,并且進(jìn)行了操作。當(dāng)我依舊使用已 free 釋放后的指針進(jìn)行下一步的操作時(shí),或者所進(jìn)行了一個(gè)計(jì)算,那么將會(huì)造成的結(jié)果天差地別,或者是其它災(zāi)難性后果。所以對于這些指針在生存期結(jié)束之后也要置為 null。查看一個(gè)示例,由于 free 釋放后依舊使用該指針,造成的計(jì)算結(jié)果天差地別:

  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include <malloc.h> 
  4. int m(char *freep) {  
  5.  int val=freep[0]; 
  6.  printf("2*freep=:%d\n",val*2); 
  7.  free(freep); 
  8.  val=freep[0]; 
  9.  printf("2*freep=:%d\n",val*2); 
  10.   
  11. int main() { 
  12.  int *freep = (int *) malloc(sizeof (int)); 
  13.  freep[0]=1; 
  14.  m(freep); 
  15.     return 0; 
  16.      

以上代碼使用 malloc 申請了一個(gè)內(nèi)存后,傳值為 1;在函數(shù)中首先使用 val 值接收 freep 的值,將 val 乘 2,之后釋放 free,重新賦值給 val,最后使用 val 再次乘 2,此時(shí)造成的結(jié)果出現(xiàn)了極大的改變,而且最恐怖的是該錯(cuò)誤很難發(fā)現(xiàn),隱蔽性很強(qiáng),但是造成的后顧難以承受。運(yùn)行結(jié)果如下:

三、 new 和 delete

C++ 中使用 new 和 delete 從堆中分配和釋放內(nèi)存,new 和 delete 是運(yùn)算符,不是函數(shù),兩者成對使用(后面說明為什么成對使用)。

new/delete 除了分配內(nèi)存和釋放內(nèi)存(與 malloc/free),還做更多的事情,所有在 C++ 中不再使用 malloc/free 而使用 new/delete。

3.1 new 和 delete 使用

new 一般使用格式如下:

  • 指針變量名 = new 類型標(biāo)識符;
  • 指針變量名 = new 類型標(biāo)識符(初始值);
  • 指針變量名 = new 類型標(biāo)識符[內(nèi)存單元個(gè)數(shù)];

在C++中new的三種用法包括:plain new, nothrow new 和 placement new。

plain new 就是我們最常使用的 new 的方式,在 C++ 中的定義如下:

  1. void* operator new(std::size_t) throw(std::bad_alloc);   
  2. void operator delete( void *) throw(); 

plain new 在分配失敗的情況下,拋出異常 std::bad_alloc 而不是返回 NULL,因此通過判斷返回值是否為 NULL 是徒勞的。

  1. char *getMemory(unsigned long size)    
  2. {     
  3.     char * p = new char[size];    
  4.     return p;  
  5. }    
  6. void main(void)    
  7.     try{    
  8.         char * p = getMemory(1000000);    // 可能發(fā)生異常 
  9.         // ...    
  10.         delete [] p;    
  11.     }    
  12.     catch(const std::bad_alloc &amp; ex)    
  13.     { 
  14.         cout &lt;&lt; ex.what(); 
  15.     }    

nothrow new 是不拋出異常的運(yùn)算符new的形式。nothrow new在失敗時(shí),返回NULL。定義如下:

  1. void * operator new(std::size_t, const std::nothrow_t&) throw(); 
  2. void operator delete(void*) throw(); 
  1. void func(unsinged long length)    
  2.     unsinged char * p = new(nothrow) unsinged char[length];    
  3.     // 在使用這種new時(shí)要加(nothrow) ,表示不使用異常處理 。 
  4.   
  5.     if (p == NULL)  // 不拋異常,一定要檢查 
  6.         cout << "allocte failed !";    
  7.         // ...    
  8.     delete [] p; 

placement new 意即“放置”,這種new允許在一塊已經(jīng)分配成功的內(nèi)存上重新構(gòu)造對象或?qū)ο髷?shù)組。placement new不用擔(dān)心內(nèi)存分配失敗,因?yàn)樗静环峙鋬?nèi)存,它做的唯一一件事情就是調(diào)用對象的構(gòu)造函數(shù)。定義如下:

  1. void* operator new(size_t, void*); 
  2. void operator delete(void*, void*); 

palcement new 的主要用途就是反復(fù)使用一塊較大的動(dòng)態(tài)分配的內(nèi)存來構(gòu)造不同類型的對象或者他們的數(shù)組。placement new構(gòu)造起來的對象或其數(shù)組,要顯示的調(diào)用他們的析構(gòu)函數(shù)來銷毀,千萬不要使用delete。

  1. void main()    
  2. {   
  3.     using namespace std;    
  4.     char * p = new(nothrow) char [4];    
  5.     if (p == NULL)    
  6.     { 
  7.         cout << "allocte failed" << endl;   
  8.         exit( -1 ); 
  9.     }    
  10.     // ...    
  11.     long * q = new (p) long(1000);    
  12.     delete []p;    // 只釋放 p,不要用q釋放。 

p 和 q 僅僅是首址相同,所構(gòu)建的對象可以類型不同。所“放置”的空間應(yīng)小于原空間,以防不測。當(dāng)”放置new”超過了申請的范圍,Debug 版下會(huì)崩潰,但 Release 能運(yùn)行而不會(huì)出現(xiàn)崩潰!

該運(yùn)算符的作用是:只要第一次分配成功,不再擔(dān)心分配失敗。

  1. void main()    
  2.     using namespace std;    
  3.     char * p = new(nothrow) char [100];    
  4.     if (p == NULL)    
  5.     {   
  6.         cout << "allocte failed" << endl; 
  7.         exit(-1); 
  8.     }    
  9.     long * q1 = new (p) long(100);    
  10.     // 使用q1  ...    
  11.     int * q2 = new (p) int[100/sizeof(int)];    
  12.     // 使用q2 ...    
  13.     ADT * q3 = new (p) ADT[100/sizeof(ADT)];    
  14.     // 使用q3  然后釋放對象 ...    
  15.     delete [] p;    // 只釋放空間,不再析構(gòu)對象。 

注意:使用該運(yùn)算符構(gòu)造的對象或數(shù)組,一定要顯式調(diào)用析構(gòu)函數(shù),不可用 delete 代替析構(gòu),因?yàn)?placement new 的對象的大小不再與原空間相同。

  1. void main()    
  2. {   
  3.     using namespace std;    
  4.     char * p = new(nothrow) char [sizeof(ADT)+2];    
  5.     if (p == NULL)    
  6.     {   
  7.         cout << "allocte failed" &lt;&lt; endl; 
  8.         exit(-1);  
  9.     }  
  10.     // ...  
  11.     ADT * q = new (p) ADT;  
  12.     // ...  
  13.     // delete q; // 錯(cuò)誤 
  14.     q->ADT::~ADT();  // 顯式調(diào)用析構(gòu)函數(shù),僅釋放對象 
  15.     delete [] p;     // 最后,再用原指針來釋放內(nèi)存 

placement new 的主要用途就是可以反復(fù)使用一塊已申請成功的內(nèi)存空間。這樣可以避免申請失敗的徒勞,又可以避免使用后的釋放。

特別要注意的是對于 placement new 絕不可以調(diào)用的 delete, 因?yàn)樵?new 只是使用別人替它申請的地方。釋放內(nèi)存是 nothrow new 的事,即要使用原來的指針釋放內(nèi)存。free/delete 不要重復(fù)調(diào)用,被系統(tǒng)立即回收后再利用,再一次 free/delete 很可能把不是自己的內(nèi)存釋放掉,導(dǎo)致異常甚至崩潰。

上面提到 new/delete 比 malloc/free 多做了一些事情,new 相對于 malloc 會(huì)額外的做一些初始化工作,delete 相對于 free 多做一些清理工作。

  1. class A 
  2.  public
  3.      A() 
  4.      { 
  5.         cont<<"A()構(gòu)造函數(shù)被調(diào)用"<<endl; 
  6.      } 
  7.      ~A() 
  8.      { 
  9.         cont<<"~A()構(gòu)造函數(shù)被調(diào)用"<<endl; 
  10.      } 

在 main 主函數(shù)中,加入如下代碼:

  1. A* pa = new A();  //類 A 的構(gòu)造函數(shù)被調(diào)用 
  2. delete pa;        //類 A 的析構(gòu)函數(shù)被調(diào)用 

可以看出:使用 new 生成一個(gè)類對象時(shí)系統(tǒng)會(huì)調(diào)用該類的構(gòu)造函數(shù),使用 delete 刪除一個(gè)類對象時(shí),系統(tǒng)會(huì)調(diào)用該類的析構(gòu)函數(shù)??梢哉{(diào)用構(gòu)造函數(shù)/析構(gòu)函數(shù)就意味著 new 和 delete 具備針對堆所分配的內(nèi)存進(jìn)行初始化和釋放的能力,而 malloc 和 free 不具備。

2.2 delete 與 delete[] 的區(qū)別

c++ 中對 new 申請的內(nèi)存的釋放方式有 delete 和 delete[] 兩種方式,到底這兩者有什么區(qū)別呢?

我們通常從教科書上看到這樣的說明:

  • delete 釋放 new 分配的單個(gè)對象指針指向的內(nèi)存
  • delete[] 釋放 new 分配的對象數(shù)組指針指向的內(nèi)存 那么,按照教科書的理解,我們看下下面的代碼:
  1. int *a = new int[10]; 
  2. delete a;        //方式1 
  3. delete[] a;     //方式2 

針對簡單類型 使用 new 分配后的不管是數(shù)組還是非數(shù)組形式內(nèi)存空間用兩種方式均可 如:

  1. int *a = new int[10]; 
  2. delete a; 
  3. delete[] a; 

此種情況中的釋放效果相同,原因在于:分配簡單類型內(nèi)存時(shí),內(nèi)存大小已經(jīng)確定,系統(tǒng)可以記憶并且進(jìn)行管理,在析構(gòu)時(shí),系統(tǒng)并不會(huì)調(diào)用析構(gòu)函數(shù)。

它直接通過指針可以獲取實(shí)際分配的內(nèi)存空間,哪怕是一個(gè)數(shù)組內(nèi)存空間(在分配過程中 系統(tǒng)會(huì)記錄分配內(nèi)存的大小等信息,此信息保存在結(jié)構(gòu)體 _CrtMemBlockHeader 中,具體情況可參看 VC 安裝目錄下 CRTSRCDBGDEL.cpp)。

針對類 Class,兩種方式體現(xiàn)出具體差異

當(dāng)你通過下列方式分配一個(gè)類對象數(shù)組:

  1. class A 
  2.    { 
  3.     private: 
  4.       char *m_cBuffer; 
  5.       int m_nLen; 
  6.  
  7.    `` public
  8.       A(){ m_cBuffer = new char[m_nLen]; } 
  9.       ~A() { delete [] m_cBuffer; } 
  10.    }; 
  11.  
  12.    A *a = new A[10]; 
  13.    delete a;         //僅釋放了a指針指向的全部內(nèi)存空間 但是只調(diào)用了a[0]對象的析構(gòu)函數(shù) 剩下的從a[1]到a[9]這9個(gè)用戶自行分配的m_cBuffer對應(yīng)內(nèi)存空間將不能釋放 從而造成內(nèi)存泄漏 
  14.    delete[] a;      //調(diào)用使用類對象的析構(gòu)函數(shù)釋放用戶自己分配內(nèi)存空間并且   釋放了a指針指向的全部內(nèi)存空間 

所以總結(jié)下就是,如果 ptr 代表一個(gè)用new申請的內(nèi)存返回的內(nèi)存空間地址,即所謂的指針,那么:

delete ptr 代表用來釋放內(nèi)存,且只用來釋放 ptr 指向的內(nèi)存。delete[] rg 用來釋放rg指向的內(nèi)存,!!還逐一調(diào)用數(shù)組中每個(gè)對象的destructor!!

對于像 int/char/long/int*/struct 等等簡單數(shù)據(jù)類型,由于對象沒有 destructor ,所以用 delete 和 delete []是一樣的!但是如果是 C++ 對象數(shù)組就不同了!

關(guān)于 new[] 和 delete[],其中又分為兩種情況:

(1) 為基本數(shù)據(jù)類型分配和回收空間;

(2) 為自定義類型分配和回收空間;

對于 (1),上面提供的程序已經(jīng)證明了 delete[] 和 delete 是等同的。但是對于 (2),情況就發(fā)生了變化。

我們來看下面的例子,通過例子的學(xué)習(xí)了解 C++ 中的 delete 和 delete[] 的使用方法

  1. #include <iostream> 
  2. using namespace std; 
  3.  
  4. class Babe 
  5. public
  6.     Babe() 
  7.     { 
  8.         cout << \"Create a Babe to talk with me\" << endl; 
  9.     } 
  10.  
  11.     ~Babe() 
  12.     { 
  13.         cout << \"Babe don\'t Go away,listen to me\" << endl; 
  14.     } 
  15. }; 
  16.  
  17. int main() 
  18.     Babe* pbabe = new Babe[3]; 
  19.     delete pbabe; 
  20.     pbabe = new Babe[3]; 
  21.     delete[] pbabe; 
  22.     return 0; 

結(jié)果是:

  1. Create a babe to talk with me 
  2. Create a babe to talk with me 
  3. Create a babe to talk with me 
  4. Babe don\'t go away,listen to me 
  5. Create a babe to talk with me 
  6. Create a babe to talk with me 
  7. Create a babe to talk with me 
  8. Babe don\'t go away,listen to me 
  9. Babe don\'t go away,listen to me 
  10. Babe don\'t go away,listen to me 

大家都看到了,只使用 delete 的時(shí)候只出現(xiàn)一個(gè) Babe don’t go away,listen to me,而使用 delete[] 的時(shí)候出現(xiàn) 3 個(gè) Babe don’t go away,listen to me。不過不管使用 delete 還是 delete[] 那三個(gè)對象的在內(nèi)存中都被刪除,既存儲位置都標(biāo)記為可寫,但是使用 delete 的時(shí)候只調(diào)用了 pbabe[0] 的析構(gòu)函數(shù),而使用了 delete[] 則調(diào)用了 3 個(gè) Babe 對象的析構(gòu)函數(shù)。

你一定會(huì)問,反正不管怎樣都是把存儲空間釋放了,有什么區(qū)別。

答:關(guān)鍵在于調(diào)用析構(gòu)函數(shù)上。此程序的類沒有使用操作系統(tǒng)的系統(tǒng)資源(比如:Socket、File、Thread等),所以不會(huì)造成明顯惡果。如果你的類使用了操作系統(tǒng)資源,單純把類的對象從內(nèi)存中刪除是不妥當(dāng)?shù)?,因?yàn)闆]有調(diào)用對象的析構(gòu)函數(shù)會(huì)導(dǎo)致系統(tǒng)資源不被釋放,這些資源的釋放必須依靠這些類的析構(gòu)函數(shù)。所以,在用這些類生成對象數(shù)組的時(shí)候,用 delete[] 來釋放它們才是王道。而用 delete 來釋放也許不會(huì)出問題,也許后果很嚴(yán)重,具體要看類的代碼了。

本文轉(zhuǎn)載自微信公眾號「C語言與CPP編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系C語言與CPP編程公眾號。

 

責(zé)任編輯:武曉燕 來源: C語言與CPP編程
相關(guān)推薦

2020-06-18 10:48:44

Linux 系統(tǒng) 數(shù)據(jù)

2018-03-28 21:40:03

2021-09-06 07:58:47

鏈表數(shù)據(jù)結(jié)構(gòu)

2024-12-31 00:00:15

2011-04-11 09:47:50

C++內(nèi)存管理

2021-06-16 00:57:16

JVM加載機(jī)制

2011-07-01 10:16:08

C++內(nèi)存管理

2023-12-12 13:13:00

內(nèi)存C++編程語言

2022-02-18 06:56:18

Wi-Fi路由器局域網(wǎng)

2020-10-09 09:49:18

HTTPS網(wǎng)絡(luò) HTTP

2019-06-22 09:50:26

2010-01-15 17:38:37

C++語言

2010-02-04 15:41:10

C++內(nèi)存管理

2023-08-09 09:03:49

CPU密集型運(yùn)算

2017-02-09 19:45:07

Linux系統(tǒng)Linux 發(fā)行版

2018-05-15 11:14:07

面試官C++編程

2010-01-14 10:34:02

C++語言

2010-01-13 17:04:36

C++語言

2010-01-14 10:23:08

C++語言

2010-01-26 10:27:43

C++語言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號