淺談 C++ 中的 new/delete 和 new[]/delete[]

來自:http://www.cnblogs.com/hazir/p/new_and_delete.html

在 C++ 中,你也許經常使用 new 和 delete 來動態申請和釋放內存,但你可曾想過以下問題呢?

  • new 和 delete 是函數嗎?
  • new [] 和 delete [] 又是什么?什么時候用它們?
  • 你知道 operator new 和 operator delete 嗎?
  • 為什么 new [] 出來的數組有時可以用 delete 釋放有時又不行?

如果你對這些問題都有疑問的話,不妨看看我這篇文章。

new 和 delete 到底是什么?

如果找工作的同學看一些面試的書,我相信都會遇到這樣的題:sizeof 不是函數,然后舉出一堆的理由來證明 sizeof 不是函數。在這里,和 sizeof 類似,new 和 delete 也不是函數,它們都是 C++ 定義的關鍵字,通過特定的語法可以組成表達式。

和 sizeof 不同的是,sizeof 在編譯時候就可以確定其返回值,new 和 delete 背后的機制則比較復雜。
繼續往下之前,請你想想你認為 new 應該要做些什么?也許你第一反應是,new 不就和 C 語言中的 malloc 函數一樣嘛,就用來動態申請空間的。你答對了一半,看看下面語句:

string *ps = new string("hello world");

你就可以看出 new 和 malloc 還是有點不同的,malloc 申請完空間之后不會對內存進行必要的初始化,而 new 可以。

所以?new?expression?背后要做的事情不是你想象的那么簡單。在我用實例來解釋 new 背后的機制之前,你需要知道?operator new?和?operator delete?是什么玩意。

operator new 和 operator delete

這兩個其實是 C++ 語言標準庫的庫函數,原型分別如下:

void *operator new(size_t);     //allocate an object
void *operator delete(void *);    //free an objectvoid *operator new[](size_t);     //allocate an array
void *operator delete[](void *);    //free an array

后面兩個你可以先不看,后面再介紹。前面兩個均是 C++ 標準庫函數,你可能會覺得這是函數嗎?請不要懷疑,這就是函數!C++ Primer?一書上說這不是重載 new 和 delete 表達式(如?operator=?就是重載?=?操作符),因為 new 和 delete 是不允許重載的。但我還沒搞清楚為什么要用 operator new 和 operator delete 來命名,比較費解。我們只要知道它們的意思就可以了,這兩個函數和 C 語言中的 malloc 和 free 函數有點像了,都是用來申請和釋放內存的,并且 operator new 申請內存之后不對內存進行初始化,直接返回申請內存的指針。

我們可以直接在我們的程序中使用這幾個函數。

new 和 delete 背后機制

知道上面兩個函數之后,我們用一個實例來解釋 new 和 delete 背后的機制:

我們不用簡單的 C++ 內置類型來舉例,使用復雜一點的類類型,定義一個類 A:

class A
{
public:A(int v) : var(v){fopen_s(&file, "test", "r");}~A(){fclose(file);}private:int var;FILE *file;
};

很簡單,類 A 中有兩個私有成員,有一個構造函數和一個析構函數,構造函數中初始化私有變量 var 以及打開一個文件,析構函數關閉打開的文件。

我們使用

class A *pA = new A(10);

來創建一個類的對象,返回其指針 pA。如下圖所示 new 背后完成的工作:

簡單總結一下:

  1. 首先需要調用上面提到的 operator new 標準庫函數,傳入的參數為 class A 的大小,這里為 8 個字節,至于為什么是 8 個字節,你可以看看《深入 C++ 對象模型》一書,這里不做多解釋。這樣函數返回的是分配內存的起始地址,這里假設是 0x007da290。
  2. 上面分配的內存是未初始化的,也是未類型化的,第二步就在這一塊原始的內存上對類對象進行初始化,調用的是相應的構造函數,這里是調用?A:A(10);?這個函數,從圖中也可以看到對這塊申請的內存進行了初始化,var=10, file 指向打開的文件
  3. 最后一步就是返回新分配并構造好的對象的指針,這里 pA 就指向 0x007da290 這塊內存,pA 的類型為類 A 對象的指針。

所有這三步,你都可以通過反匯編找到相應的匯編代碼,在這里我就不列出了。

好了,那么 delete 都干了什么呢?還是接著上面的例子,如果這時想釋放掉申請的類的對象怎么辦?當然我們可以使用下面的語句來完成:

delete pA;

delete 所做的事情如下圖所示:

delete 就做了兩件事情:

  1. 調用 pA 指向對象的析構函數,對打開的文件進行關閉。
  2. 通過上面提到的標準庫函數 operator delete 來釋放該對象的內存,傳入函數的參數為 pA 的值,也就是 0x007d290。

好了,解釋完了 new 和 delete 背后所做的事情了,是不是覺得也很簡單?不就多了一個構造函數和析構函數的調用嘛。

如何申請和釋放一個數組?

我們經常要用到動態分配一個數組,也許是這樣的:

string *psa = new string[10];      //array of 10 empty strings
int *pia = new int[10];           //array of 10 uninitialized ints

上面在申請一個數組時都用到了?new []?這個表達式來完成,按照我們上面講到的 new 和 delete 知識,第一個數組是 string 類型,分配了保存對象的內存空間之后,將調用 string 類型的默認構造函數依次初始化數組中每個元素;

第二個是申請具有內置類型的數組,分配了存儲 10 個 int 對象的內存空間,但并沒有初始化。

如果我們想釋放空間了,可以用下面兩條語句:

delete [] psa;
delete [] pia;

都用到?delete []?表達式,注意這地方的 [] 一般情況下不能漏掉!我們也可以想象這兩個語句分別干了什么:第一個對 10 個 string 對象分別調用析構函數,然后再釋放掉為對象分配的所有內存空間;第二個因為是內置類型不存在析構函數,直接釋放為 10 個 int 型分配的所有內存空間。

這里對于第一種情況就有一個問題了:我們如何知道 psa 指向對象的數組的大小?怎么知道調用幾次析構函數?

這個問題直接導致我們需要在 new [] 一個對象數組時,需要保存數組的維度,C++ 的做法是在分配數組空間時多分配了 4 個字節的大小,專門保存數組的大小,在 delete [] 時就可以取出這個保存的數,就知道了需要調用析構函數多少次了。

還是用圖來說明比較清楚,我們定義了一個類 A,但不具體描述類的內容,這個類中有顯示的構造函數、析構函數等。那么 當我們調用

class A *pAa = new A[3];

時需要做的事情如下:

從這個圖中我們可以看到申請時在數組對象的上面還多分配了 4 個字節用來保存數組的大小,但是最終返回的是對象數組的指針,而不是所有分配空間的起始地址。

這樣的話,釋放就很簡單了:

delete []pAa;

這里要注意的兩點是:

  • 調用析構函數的次數是從數組對象指針前面的 4 個字節中取出;
  • 傳入?operator delete[]?函數的參數不是數組對象的指針 pAa,而是 pAa 的值減 4。

為什么 new/delete 、new []/delete[] 要配對使用?

其實說了這么多,還沒到我寫這篇文章的最原始意圖。從上面解釋的你應該懂了 new/delete、new[]/delete[] 的工作原理了,因為它們之間有差別,所以需要配對使用。但偏偏問題不是這么簡單,這也是我遇到的問題,如下這段代碼:

int *pia = new int[10];
delete []pia;

這肯定是沒問題的,但如果把?delete []pia;?換成?delete pia;?的話,會出問題嗎?

這就涉及到上面一節沒提到的問題了。上面我提到了在?new []?時多分配 4 個字節的緣由,因為析構時需要知道數組的大小,但如果不調用析構函數呢(如內置類型,這里的 int 數組)?我們在?new []?時就沒必要多分配那 4 個字節, delete [] 時直接到第二步釋放為 int 數組分配的空間。如果這里使用?delete pia;那么將會調用?operator delete?函數,傳入的參數是分配給數組的起始地址,所做的事情就是釋放掉這塊內存空間。不存在問題的。

這里說的使用?new []?用 delete 來釋放對象的提前是:對象的類型是內置類型或者是無自定義的析構函數的類類型!

我們看看如果是帶有自定義析構函數的類類型,用?new []?來創建類對象數組,而用 delete 來釋放會發生什么?用上面的例子來說明:

class A *pAa = new class A[3];
delete pAa;

那么?delete pAa;?做了兩件事:

  • 調用一次 pAa 指向的對象的析構函數;
  • 調用?operator delete(pAa);?釋放內存。

顯然,這里只對數組的第一個類對象調用了析構函數,后面的兩個對象均沒調用析構函數,如果類對象中申請了大量的內存需要在析構函數中釋放,而你卻在銷毀數組對象時少調用了析構函數,這會造成內存泄漏。

上面的問題你如果說沒關系的話,那么第二點就是致命的了!直接釋放 pAa 指向的內存空間,這個總是會造成嚴重的段錯誤,程序必然會奔潰!因為分配的空間的起始地址是 pAa 指向的地方減去 4 個字節的地方。你應該傳入參數設為那個地址!

同理,你可以分析如果使用 new 來分配,用?delete []?來釋放會出現什么問題?是不是總會導致程序錯誤?

總的來說,記住一點即可:new/delete、new[]/delete[] 要配套使用總是沒錯的!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458605.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458605.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458605.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JavaScript中的原型繼承原理

在JavaScript當中,對象A如果要繼承對象B的屬性和方法,那么只要將對象B放到對象A的原型鏈上即可。而某個對象的原型鏈,就是由該對象開始,通過__proto__屬性連接起來的一串對象。__proto__屬性是JavaScript對象中的內部屬性&#xf…

XMPP

XMPP不支持視頻聊天 支持文字聊天 但對于圖片和語音聊天支持的不好 那么就將他們轉成NSData的形式 IM Instance Message 即時聊天 聊天系統中 XMPP主要做兩個人的聊天和群聊,只用了這兩個功能 還有一個HTTP的服務器 ,他是一個旁路服務器 XMPP底層…

QT 調試

QT調試(參考下面的說明就可以正常調試):http://blog.csdn.net/wchengshen/article/details/50254731http://blog.csdn.net/sx341125/article/details/53606534 QT調用DLL: Qt中調用VS編譯dll的方法(一)----顯式調用 qt使用動態庫(…

建模元件有哪些在MapleSim中

信號庫:包含通用信號模塊、布爾、控制器、離散信號模塊、信號源、線性信號模塊、非線性信號模塊、時間離散信號模塊、查詢表、信號轉換器、數學運算、關系元件、特殊信號模塊,應用案例。 電子庫:包含電阻、運算放大器、二極管、步進電機、模擬…

iOS小筆記

controller:連接二者的橋梁;cocoa frameworks 有兩個框架:foundationfoundation 是cocoa中最基本的一些類;再mac應用程序中負責對象管理,內存管理,容器等相關數據;uikit:uikit&…

【C++】VS2010將寫好的程序打包成安裝文件發布

參考鏈接:http://blog.csdn.net/yongh701/article/details/51326142 我們可以將自己寫好的VS2010程序打包成安裝文件,給用戶安裝,具體步驟如下: 1、如下圖,同樣是新建一個項目,但是這次是新建一個其它項目…

01_jeecms建站

一、環境安裝 JDK5TOMCAT5.5MYSQL5及以上http://www.jeecms.com/tutorial/index.jhtml參考環境安裝篇二、解壓文件安裝包jeecms-v5zip,如圖圖1ROOT文件夾復制放到tomcat下的webapps文件夾(注:請先刪除webapps下原有的默認ROOT文件夾)如不想部…

WiFi基本知識

轉自:http://blog.csdn.net/myarrow/article/details/7930131 1. IE802.11簡介 標準號IEEE 802.11bIEEE 802.11aIEEE 802.11gIEEE 802.11n標準發布時間1999年9月1999年9月2003年6月2009年9月工作頻率范圍2.4-2.4835GHz 5.150-5.350GHz5.475&a…

iOS各種小理論知識

Objective-C 部分 1. 你如何理解 OC 的內存管理 OC 內存管理是基于引用計數。誰想使用某個對象 B,就要把對象 B 的計數器1,如果不 使用這個對象了,那么就把對象 B 計數器-1,如果 B 對象計數器減到 0,那么 B 對象自動會調用自己的 dealloc 函數,也就是這個對象被銷毀。 一…

libev 宏展開

想看源碼,宏太多,看著累,宏展開,再看,功力時間不夠,先放下 放上宏展開后的代碼。 libev4.20 展開方示為 ./configure 修改makefile文件,字符串 替換CC為 CPP 注意要把基礎的CC定義保留 make mv …

FreeRTOS高級篇7---FreeRTOS內存管理分析

原文:http://blog.csdn.net/zhzht19861011/article/details/51606068 內存管理對應用程序和操作系統來說都非常重要。現在很多的程序漏洞和運行崩潰都和內存分配使用錯誤有關。 FreeRTOS操作系統將內核與內存管理分開實現,操作系統內核僅規定了必要的內…

筆試題

1.是不是一個父類寫了一個virtual 函數,如果子類覆蓋它的函數不加virtual ,也能實現多態? virtual修飾符會被隱形繼承的。private 也被集成,只事派生類沒有訪問權限而已。virtual可加可不加。子類的空間里有父類的所有變量(static除外)。同一個函數只存…

FreeRTOS學習筆記——互斥型信號量

來自:http://blog.csdn.net/xukai871105/article/details/43456985 0.前言 在嵌入式操作系統中互斥型信號量是任務間資源保護的重要手段。下面結合一個具體例子說明FreeRTOS中的互斥型信號量如何使用。 【相關博文】 【FreeRTOS STM32移植筆記】 【FreeRTOS學習筆記…

JSP動態網站環境搭建應用中的詳細步驟(Tomcat和Apache/IIS的整合)

鏈接地址:http://www.cnblogs.com/dartagnan/archive/2011/03/25/2003426.html JSP動態網站環境搭建應用中的詳細步驟(Tomcat和Apache/IIS的整合)sun推出的JSP(Java Server Pages)是一種執行于服務器端的動態網頁開發技術,它基于J…

關于JS獲取select值的兩種實現方法

前幾天發了一篇關于javascript獲取select值的方法&#xff0c;后來發現有另一種實現方法&#xff0c;所以就都發出來比較一下&#xff1a; 方法一&#xff1a;通過獲取option標簽的value值來確定&#xff1a; <!DOCTYPE html> <html> <head><meta charset…

c語言題庫1

1. 用預處理指令#define 聲明一個常數&#xff0c;用以表明1年中有多少秒&#xff08;忽略閏年問題&#xff09; #define time (365*24*60*60) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365) 2. 寫一個“標準”宏MIN&#xff0c;這個宏輸入兩個參數并返回較小的一個。 #di…

FreeRTOS系列第19篇---FreeRTOS信號量

來自&#xff1a;http://blog.csdn.net/zhzht19861011/article/details/50835613 本文介紹信號量的基礎知識&#xff0c;詳細源碼分析見《FreeRTOS高級篇6---FreeRTOS信號量分析》 1.信號量簡介 FreeRTOS的信號量包括二進制信號量、計數信號量、互斥信號量&#xff08;以后簡稱…

mysql語法替換字符串

UPDATE ht_business_task SET url REPLACE ( url, &amp;, & )轉載于:https://www.cnblogs.com/lz20150121/p/5030739.html

POJ1274 The Perfect Stall(二分圖)

題意&#xff1a; 一些奶牛只有在特定的圍欄中才能產奶&#xff0c;要求合理安排使能產奶的奶牛數達到最大。 要點&#xff1a; 二分圖裸題&#xff0c;最近剛學了二分圖&#xff0c;看下面的參考博客&#xff0c;寫的比較好&#xff1a; 參考博客&#xff1a;匈牙利算法 15479…

藍牙HCI剖析(一)

來自&#xff1a;http://blog.csdn.net/xiaoxiaopengbo/article/details/51334257 一.HCI介紹 HCI提供了訪問bluetooth control的統一接口&#xff0c;通俗來講&#xff0c;就是定義了特定的格式來控制藍牙芯片來做相應的動作&#xff08;比如inquiry,connect,disconnect&#…