1. 程式人生 > >placement new 為什麼不能用delete來刪除

placement new 為什麼不能用delete來刪除

https://blog.csdn.net/D_Guco/article/details/54019495

    在c++中new和delete操作是我們操作記憶體最常用的一對操作符,在使用new時編譯器會申請記憶體,然後呼叫類的建構函式來初始化物件,呼叫delete會銷燬物件同時釋放該物件佔用的記憶體,並且我們可以過載new操作符。

    還有一個不常用的new操作placement new,在使用時需要我們傳入一個指標,此時會在該指標指向的記憶體空間構造該物件,該指標指向的地址可以使堆空間,棧空間,也可以是靜態儲存區。

    如果有這樣一個場景,我們需要大量的申請一塊類似的記憶體空間,然後又釋放掉,比如在在一個server中對於客戶端的請求,每個客戶端的每一次上行資料我們都需要為此申請一塊記憶體,當我們處理完請求給客戶端下行回覆是釋放掉該記憶體,表面上看者符合c++的記憶體管理要求,沒有什麼錯誤,但是仔細想想很不合理,為什麼我們每個請求都要重新申請一塊記憶體呢,要知道每一次內從的申請,系統都要在記憶體中找到一塊合適大小的連續的記憶體空間,這個過程是很慢的(相對而言),極端情況下,如果當前系統中有大量的記憶體碎片,並且我們申請的空間很大,甚至有可能失敗。為什麼我們不能共用一塊我們事先準備好的記憶體呢?可以的,我們可以使用placement new來構造物件,那麼就會在我們指定的記憶體空間中構造物件。

在這裡插入圖片描述



  
   
  
 
 
      這裡我們可以看到,物件的記憶體地址和我們事先申請的記憶體地址是一樣的。對於
  我們實現分配的記憶體大小,官方說要用物件大小+sizeof(int)來儲存,但是我試了一下用物件大小的儲存空間也可以,其次是該儲存空間的釋放,就按照該儲存空間的型別相對應的方法取釋放,區域性的當出了作用域後自動釋放,堆空間手動釋放等等,另外當我們呼叫placement new構造的物件,在物件的銷燬時需要我們手動呼叫解構函式,此時會清空記憶體中的內容,並不會釋放該物件的記憶體空間,觀察呼叫解構函式呼叫前後的列印區別可知。
   其次我們要注意一點,我們通過placement new構造的物件,不能呼叫delete來釋放記憶體,因為我們構造時其實是沒有申請記憶體的,只是呼叫了建構函式,返回了一個指標指向了我們事先準備好的記憶體空間,當我們使用delete編譯器會試圖去刪除物件在堆上的記憶體空間,但是此時的物件並不在堆上,可能在棧上或者在靜態儲存區(每個儲存區的地址範圍不同,我們很可能拿著一個棧上的地址去堆上刪除),此時程式會掛掉(註釋2)。     最後我們要注意的是如果我們要用這種方式來替換普通的構造物件的方式,我們要保證同意時刻只有一個物件構造,比如開始時說的場景中同一時刻只能處理一個訊息,也就是說是一個單程序單執行緒的模型,當我們處理完一個訊息後,這個訊息對於我們沒有意義了,當下一個訊息來時,我們就可以重新構造物件了,否則就會出現我們正在使用的物件被修改的的現象,當然我們也可以每個執行緒一塊記憶體空間。

    在c++中new和delete操作是我們操作記憶體最常用的一對操作符,在使用new時編譯器會申請記憶體,然後呼叫類的建構函式來初始化物件,呼叫delete會銷燬物件同時釋放該物件佔用的記憶體,並且我們可以過載new操作符。

    還有一個不常用的new操作placement new,在使用時需要我們傳入一個指標,此時會在該指標指向的記憶體空間構造該物件,該指標指向的地址可以使堆空間,棧空間,也可以是靜態儲存區。

    如果有這樣一個場景,我們需要大量的申請一塊類似的記憶體空間,然後又釋放掉,比如在在一個server中對於客戶端的請求,每個客戶端的每一次上行資料我們都需要為此申請一塊記憶體,當我們處理完請求給客戶端下行回覆是釋放掉該記憶體,表面上看者符合c++的記憶體管理要求,沒有什麼錯誤,但是仔細想想很不合理,為什麼我們每個請求都要重新申請一塊記憶體呢,要知道每一次內從的申請,系統都要在記憶體中找到一塊合適大小的連續的記憶體空間,這個過程是很慢的(相對而言),極端情況下,如果當前系統中有大量的記憶體碎片,並且我們申請的空間很大,甚至有可能失敗。為什麼我們不能共用一塊我們事先準備好的記憶體呢?可以的,我們可以使用placement new來構造物件,那麼就會在我們指定的記憶體空間中構造物件。