C 中各種new delete總結

2021-07-09 01:42:28 字數 4580 閱讀 3397

c++語言提供了三種關於new、delete的操作或者概念,這三種分別體現了c++語言對記憶體進行操作的不同層次,深入理解這三種情形,對於不論是個人開發中對記憶體進行管理或者是閱讀其他**都是乙個基礎。特在此進行總結。

凡是涉及到對記憶體進行操作的時候,如果新開始配置,那麼對其進行細化都可以分為三個步驟:

1. 分配一段原始記憶體,其上可以儲存任意型別的資料物件(也就是可以理解為對應的指標型別是void * );

2. 構造乙個指定型別的物件,不論是語言原生支援的型別還是使用者自定義型別,都會呼叫建構函式進行該物件的構造;

3. 將構造的物件儲存到分配的原始記憶體上,讓指向改記憶體的指標型別從void * 變為相應物件型別的指標型別。

同理,如果是使用完成進行釋放記憶體,那麼細化的步驟與上述相反:

1呼叫物件的析構函式銷毀這個物件;

2將堆上使用的記憶體釋放歸還給作業系統。

new、delete運算子,從字面上看,這是一種語言提供的特殊運算子,與「+、-、*、/」這種算術運算子屬於同乙個範疇,也就是c++語言規範提供的,開發者是不能進行改動的,只能被動的使用。

new operator實現上述提到的所有三個步驟:從堆上掃瞄可使用的記憶體進行分配,呼叫相應型別的建構函式構造乙個物件,最後將物件儲存到相應的堆記憶體上。delete operator同樣實現了上述所有細化的步驟。

operator new/delete中的operator與new/delete就是c++語言提供的運算子過載語法,與「operator ++,operator ==」這種運算子過載是同乙個範疇,二者合在一起作為運算子過載的函式名稱。

以自定義類的乙個運算子過載成員函式出現,實現自定義額外功能後,一般通過呼叫全域性的operator new函式,來為所屬類的物件分配指定大小的記憶體,因為是類的乙個成員函式,因此不會呼叫類的建構函式。

進行記憶體分配時,如果無法滿足要求會有如下行為:

(1)如果有new_handler就呼叫之,否則

(2)如果沒有要求不丟擲異常(沒有使用nothrow表示式),就執行bad_alloc異常,否則

(3)返回0

如果需要對某乙個自定義的類的記憶體分配做出個性化的設定,就可以利用這個operator new/delete運算子過載函式來重寫,需要注意的是:

(1)operator new過載函式的返回值必須宣告為void *,operator delete的返回值必須宣告為void;

(2)operator delete的引數必須為通過operator new分配的記憶體對應的指標,且要轉換為void * 型別後呼叫;

(3)operator new的第乙個引數必須為請求分配的空間的位元組大小,型別為size_t。

全域性的operator new和operator delete可以看做是malloc和free函式,但是它們不能交叉使用,因為他們進行記憶體登記的方式不同。乙個類的operator new和operator delete可以看做是申請該類物件的記憶體分配方法。

#include 

#include

using

namespace

std;

class a

~a()

void * operator

new(size_t s, string str)

void

operator

delete(void * p)

};int main(int argc, char * argv)

執行結果:

user@ubuntu:~$ g++ -o newdelete newdelete.cpp

user@ubuntu:~$ ./newdelete

operator

new: size 1, other param this the other param string

constructor

called

destructor

called

operator

delete

called

上面例項中,通過為自定義的型別a定義operator new 和operator delete函式,在主函式使用普通的new、delete運算子時,會自動呼叫類a的operator new和operator delete函式,與此同時,主函式中的new和delete還會去呼叫類a的建構函式和析構函式。

其中,如果只是使用new a這樣申請乙個物件,那麼會傳到operator new函式的第乙個引數,如果是申請乙個陣列,那麼對應的位元組數也會自動傳遞過去。這也是operator new的第乙個引數必須為申請記憶體的大小的原因。

placement new是operator new的乙個標準過載函式,不夠被自定義的版本替換。也就是operator new全域性函式的乙個特殊過載版本,根據過載函式的含義,placement new的引數是確定的兩個引數,與全域性的operator new僅乙個size引數和自定義operator new任意引數不同。函式定義如下:

void * operator

new(size_t, void *p) throw()

其忽略了第乙個引數size,只返回第二個引數,也就是沒有使用第乙個size引數去在堆中申請記憶體,而是只呼叫建構函式,並繫結到傳入的第二個預先配置好的記憶體的指標引數上。

由於多了第二個引數,因此在程式中使用時需要指定這個引數,就與上述例子中傳遞乙個string的額外引數類似。標準使用步驟如下:

char * buf = new

char[sizeof(b)];

b *pb = new (buf) b();

pb->~b();

delete buf;

根據前面的分析,這個函式的主要功能是構建乙個物件並放置到指定的記憶體位置上,也就是實現了前面所述的第二和第三個步驟。這裡面通過忽略第乙個分配記憶體大小引數跳過記憶體的分配,在已經分配好的記憶體上構造物件就是placement new最大的優勢。

所有類的析構函式都可以系統自動呼叫,但是唯有使用placement new構造的物件需要顯式呼叫析構函式。

placement new呼叫建構函式之後將物件繫結到傳入的指標指向的記憶體中,這裡並沒有對記憶體的型別做限制,也就是既可以是棧、也可以是堆。但是使用operator new或者new運算子都是只能在系統的堆上進行記憶體分配的。

1. 用placement new 解決buffer的問題

問題描述:用new分配的陣列緩衝時,由於呼叫了預設建構函式,因此執行效率上不佳。若沒有預設建構函式則會發生編譯時錯誤。如果你想在預分配的記憶體上建立物件,用預設的new操作符是行不通的。要解決這個問題,你可以用placement new構造。它允許你構造乙個新物件到預分配的記憶體上。

2. 增大時空效率的問題

使用new操作符分配記憶體需要在堆中查詢足夠大的剩餘空間,顯然這個操作速度是很慢的,而且有可能出現無法分配記憶體的異常(空間不夠)。placement new就可以解決這個問題。我們構造物件都是在乙個預先準備好了的記憶體緩衝區中進行,不需要查詢記憶體,記憶體分配的時間是常數;而且不會出現在程式執行中途出現記憶體不足的異常。所以,placement new非常適合那些對時間要求比較高,長時間執行不希望被打斷的應用程式。

1 預分配快取的記憶體

class b;

char *buf = new

char[sizeof(b)]; //new char[sizeof(b) * n]

(2)使用棧,自動變數宣告

class

b;char buf[sizeof(b)]; //buf[n * sizeof(b)]

(3)直接使用有效的記憶體位址分配

void * buf = reinterpret_cast

(0x00004072);

2 呼叫placement new進行物件構造和繫結

b *pb = new (buf) b(); //new (buf) b(a, b)
3 正常使用該物件

pb->method1(...);

pb->method2(...);

pb->method3(...);

...

4 顯式呼叫析構函式

pb->~b();
5 銷毀快取區

緩衝區可以反覆使用,這也是使用placement new比普通的new更快的原因,當使用完成,不需要快取區時,就需要根據當時申請這個快取區的方式進行釋放。

//用new在堆上申請的,需要顯式釋放

delete buf;

//在棧上分配的和直接位址轉換的程式會自動**,不需要顯式釋放。

C 中各種容器特點總結

分為順序容器和關聯容器。順序容器包括 1 vector 內部資料結構 陣列,可隨機訪問元素,在末尾增加或刪除元素與元素數目無關,在其 他部分增加或刪除元素隨著元素數目呈線性變化。可通過reserve提前分配足夠的記憶體。2 deque 雙端佇列,按頁 塊來分配儲存,每頁 塊包含固定的數目的元素。支援...

c 中的過載全域性new,delete

這樣我們每次呼叫new,比如int pn new int 被編譯器替換成了int pn new file line int,從而呼叫我們定義的operator new,這種辦法確實很妙。需要交代的是,對於陣列同樣適用,而是在編譯的是後由編譯器計算出所需要的長度呼叫我們定義的operator new函...

C 中的各種Access操縱總結

新建表 create table 表名 自動編號字段 int identity 1,1 primary key 欄位1 nvarchar 50 default 預設值 null 欄位2 ntext null 欄位3 datetime,欄位4 money null 欄位5 int default 0,...