深入理解C語言的new 和delete

2022-09-24 13:12:15 字數 3914 閱讀 7739

目錄

c++的動態記憶體管理方式和c語言不一樣,在c++中使用new和delete來替換c語言中的malloc和free。這裡有幾個點不一樣,

1、new和delete是操作符,malloc和free是函式(我的理解是c++將new和delete約定為操作符而已,new和delete操作符過載函式本質上還是函式)

2、c++有了類的概念,類物件的初始化除了要分配記憶體,還需要對記憶體進行初始化!所以,c++必須引入一種新的記憶體分配方式,既可以像malloc一樣開闢記憶體,還要能夠呼叫類物件的建構函式(delete的引入同理)。

3、new和delete是c++完全新增的記憶體操程式設計客棧作符,他們和new和delete也是有不一樣的地方。

下面,咱們來一一講解⬇

既然new和delete都是操作符,咱們可以對new和delete進行過載;當你再使用new和delete操作記憶體時,編譯器就會呼叫到咱們自己過載的new/delete全域性函式了。(如對操作符過載不了解的,請自行補充知識)

void* operator new(size_t size)

std::cout << "new memory size = " << size << " address = " << ptr << std::endl;

程式設計客棧return ptr;

}void* operator new(size_t size)

std::cout << "new memory size = " << size << " address = " << ptr << std::endl;

return ptr;

}void operator delete(void* ptr)

void operator delete(void* ptr)

此時,再使用 int* p = new int ; 開闢記憶體,那麼,c++編譯器會自動鏈結到我們剛才的操作符過載函式 void* operator new(size_t size) ,至於編譯器是怎麼將 int* p = new int ; 解析成 void* operator new(size_t size) 函式的,咱們不關心,咱們只要知道編譯器做了這樣一www.cppcns.com層**解析轉換即可。並且,轉換的過程中會將需要開闢的記憶體大小當作形式引數傳遞過去!

這裡還有一點需要注意:函式的返回值是void*,而咱們new操作接收的返回值為int*,這裡其實也是編譯在底層做了一層轉換!(非常重要,new 的返回值和 operator new 函式的返回並不完全相同,包括型別和位址)

在開始驗證new和delete之前,咱們先使用以下**:

#include

#include

#include

using namespace std;

//此處新增前面一步過載new和delete的**

class master

~master()

public:

int c;

};int main()

; delete p;

return 0;

}推薦乙個**除錯cpp的**:compiler explorer (godbolt.org)

master * p = new master{};這一行的彙編資訊如下:

這裡有3個點要注意,

www.cppcns.com

那麼,delete操作的彙編資訊應該和new剛好相反,這個可以自行測試!

下面將咱們過載的operator方法新增進去,然後執行,output日誌資訊如下:

new memory size = 4 address = 0x2154eb0

constructor ---- 0x2154eb0

destructor ---- 0x2154eb0

delete 0x2154eb0

通過日誌,咱們也能夠得出這樣的結論:

new: 先malloc記憶體,然後執行建構函式 delete:先執行析構函式,然後free記憶體

前面咱們說了new/delete與new/delete有一些不同,那我們先看看日誌資訊,請新增以下測試**:

int main()

; //測試new

master * parray = new master[5];

printf("parray = %p\n", parray);

printf("parray[0] = %p\n", &parray[0]);

printf("parray[1] = %p\n", &parray[1]);

// delete parray;

// delete p;

return 0;

}///

output:---------------------------------------begin

new memory size = 4 address = 0x565422925eb0

constructor ---- 0x565422925eb0

new memory size = 28 address = 0x5654229262e0

constructor ---- 0x5654229262e8

constructor ---- 0x5654229262ec

constructor ---- 0x5654229262f0

constructor ---- 0x5654229262f4

constructor ---- 0x5654229262f8

parray = 0x5654229262e8

parray[0] = 0x5654229262e8

parray[1] = 0x5654229262ec

output:----------------------------------------end

重點來了,malloc返回的記憶體位址(0x5654229262e0)和new返回的位址(0x5654229262e8)並不一致,而且申請的記憶體大小並不是master類的大小 4*5=20,而是28!!

通過彙編**,我們看到這裡確實偏移了8個位元組!其他的操作與new沒有多大差別,都是先呼叫operator new,然後再迴圈呼叫陣列中的每個物件的建構函式!

通過output日誌和彙編分析,可以得到我們前面講到的結論:(非常重要,new 的返回值和 operator new 函式的返回並不完全相同,包括型別和位址)!

我們呼叫 delete parray; 刪除的位址為 0x5654229262e8,而真實觸發 operator delete 的形參為 0x5654229262e0, 詳細日誌資訊如下:

也就是operator new 和 operator delete 的操作的記憶體是真實位址,並且是正確的!為什麼會這樣?就是因為c++編譯器在底層做了一次轉換!

這個問題也很好理解,如果編譯器沒有這樣一層邏輯轉換,當你delete乙個陣列時,編譯器要怎麼操作呢?他怎麼知道陣列的長度然後遍歷去呼叫析構函式呢?所以,這樣一層邏輯轉換有了存在的意義,這也體現在 new 陣列時為什麼大小並不是 = 物件大小*陣列長度,而是多了8個位元組!

好了嘛,那多出來的8位元組是不是就是陣列長度呢?咱們可以把這塊記憶體資訊輸出!新增以下**:

unsigned long* psize = (unsigned long*)parray - 1;//偏移8位元組

printf("p = %p\n", psize);

printf("sizeof parray = %d\n", *psize);//輸出該位址上的記憶體資料

日誌輸出:

偏移8位元組之後就是咱們 operator new 函式分配的真實的記憶體位址,然後咱們輸出該位址8位元組空間的資料資訊為5,這就是申請的資料空間的大小!

也就是有了這個多餘的8位元組(在32位系統上為4位元組),new 儲存陣列長度到該記憶體, delete 時就能夠從該記憶體讀取陣列長度,並遍歷陣列去呼叫析構函式啦!

本文標題: 深入理解c語言的new和delete

本文位址:

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...

C語言指標深入理解

前幾天看了乙個程式,裡面一段關於指標的 讓我非常糾結,看了很久才看懂,在這裡將將其記錄下來,希望能對大家有一定的幫助,先看示例程式 編譯器gcc include include include typedef struct list node list node,list,plist node st...