<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
有時候我們覺得,C++術語彷彿是要故意讓人難以理解似的。這裡就有一個例子:請說明new operator 和 operator new 之間的差異。
上面這段話出自《More Effective C++》中的條款8。有興趣的讀者可以閱讀這本書。現在就讓我們揭開這神祕的面紗吧。
new
是C++的一個關鍵字、操作符。
當我們執行Test* pt = new Test();
這句程式碼時,實際上幹了三件事情:
為什麼這麼說呢?口說無憑眼見為實,請接著往下看。
首先我們需要寫一個空類,然後在main中new出這個類。程式碼可參考如下:
class A { public: A() { } ~A() { } }; int main() { A* p = new A(); delete p; p = nullptr; return 0; }
第一步:在建立這一行新增斷點(可左擊該行行首或者在該行按F9即可)。
第二步:開始偵錯到當前斷點處(可按F5)。
第三步:在上方功能欄中點選【Debug】->【Windows】->【Disassembly】。中文對應的是【偵錯】->【視窗】->【反組合】。詳細請看下圖。
操作完上面三步之後我們就到了組合程式碼。由於重點不是研究組合語言,所以這裡我就僅對上面那三步進行標記。驗證一下上面的一個猜想。
那麼這裡我們用到的new操作符,也就是new operator
,在《C++ Primer》書中也被稱為new expression
。
功能:只負責記憶體分配
operator new預設情況下呼叫分配記憶體的程式碼,去嘗試在堆區獲取一段空間,如果成功就返回,如果失敗,則呼叫new_hander
。有關new_hander我之前寫了一篇:new_hander文章連結
下面對operator new過載,進行測試;
class A { public: A() { std::cout << "Call A Constructor!" << std::endl; } ~A() { std::cout << "Call A Destructor!" << std::endl; } void* operator new(size_t size) { std::cout << "Call operator new" << "t size = " << size << std::endl; return ::operator new(size); // 通過::operator new呼叫了全域性的new } }; int main() { A* pt = new A(); delete pt; pt = nullptr; return 0; }
執行結果:
可以看到先列印類內的operator new再呼叫constructor函數最後呼叫destructor函數。
若要過載全域性的::operator new
時,最後就不能return
自身了需要寫成malloc(size)
。對應的delete
也有delete operator
和 operator delete
倆種,operator delete
也是可以過載的。所以一般來說過載了operator new
就需要過載對應的operator delete
了。
具體請看下面的程式碼:
新增一個全域性的operator new函數
void* operator new(size_t size) { std::cout << "Call global operator new" << "t" << size << std::endl; return malloc(size); }
執行結果:
該函數我們可以進行過載,但是第一引數的型別必須是size_t。而且我們還可以單獨呼叫operator new
。將返回一個void型別的指標。
在原有程式碼基礎上,增加一個成員函數用於輸出紀錄檔。
class A { public: A() { std::cout << "Call A Constructor!" << std::endl; } ~A() { std::cout << "Call A Destructor!" << std::endl; } void* operator new(size_t size) { std::cout << "Call operator new" << "t size = " << size << std::endl; return ::operator new(size); // 通過::operator new呼叫了全域性的new } void print() { std::cout << "ha ha !" << std::endl; } }; void* operator new(size_t size) { std::cout << "Call global operator new" << "t size = " << size << std::endl; return malloc(size); } int main() { void* rawMemory = operator new(sizeof(A)); A* pa = static_cast<A*>(rawMemory); pa->print(); delete pa; pa = nullptr; return 0; }
執行結果:
可以看到只列印了全域性的operator new函數已經解構函式。
標頭檔案:#include <new> 或者#include <new.h>
可以直接呼叫constructor函數,是operator new的一個特殊版本,也被稱為placement new
函數。
需要實現一個void* operator new(size_t, void* location)
的過載版本。不需要申請記憶體只需要返回當前物件即可。
呼叫的語法:new(ObjectName) ClassName(建構函式的引數)
class A { public: A() { std::cout << "Call A Constructor!" << std::endl; } ~A() { std::cout << "Call A Destructor!" << std::endl; } void* operator new(size_t size) { std::cout << "Call operator new" << "t size = " << size << std::endl; return ::operator new(size); // 通過::operator new呼叫了全域性的new } void* operator new(size_t size, void* location) { std::cout << "Call operator new(size_t size, void* location)" << std::endl; return location; } void print() { std::cout << "ha ha !" << std::endl; } }; int main() { void* rawMemory = operator new(sizeof(A)); A* pa = static_cast<A*>(rawMemory); // 建立記憶體 new(pa) A(); // 呼叫建構函式 pa->print(); delete pa; pa = nullptr; return 0; }
執行結果:
這裡的operator new的目的是要為物件找記憶體,然後返回一個指標指向它。在placement new的情況下,呼叫者已經知道指向記憶體的指標了,所以placement new唯一需要做的就是將已獲得指標進行返回。雖然說size_t引數沒有用到但是必須要加,之所以不給形參名是因為防止編譯器抱怨“某某變數未被使用”。
為了避免記憶體漏失,每一個動態分配都必須匹配一個釋放動作。
記憶體釋放的動作是由operator delete
執行,函數原型:void operator delete(void* object);
當我們寫了這句程式碼時delete pa;
實際上執行了倆件事。
1、呼叫destructor函數
2、釋放物件所佔的記憶體資源
轉換成程式碼就相當於:
pa->~A(); operator delete(pa);
當我們在建立物件時,沒有呼叫constructor
函數,那麼釋放記憶體時也不需要呼叫destructor
函數。只需要operator delete(pa);
。
int main() { void* rawMemory = operator new(sizeof(A)); . ...其他程式碼 operator delete(rawMemory); return 0; }
上面這段程式碼其實就等價於C語言裡面呼叫malloc和free函數。
如果使用placement new
在記憶體中產生物件,我們不能使用delete operator
,因為會呼叫operator delete
函數來釋放記憶體。首先該記憶體並不是由該物件的operator new
函數分配而來。它僅僅做了一個返回而已,所以這種情況下只需要呼叫destructor
函數即可。
int main() { void* rawMemory = operator new(sizeof(A)); A* pa = static_cast<A*>(rawMemory); // 建立記憶體 new(pa)A(); // 呼叫建構函式 pa->~A(); pa = nullptr; operator delete(rawMemory); return 0; }
在上面這段程式碼中,pa物件就是使用placement new
,所以最後只需要呼叫destructor
函數。
當我們使用A* pa = new A[10];
這段程式碼時,分配記憶體的方式將會發生變化。
1、由operator new
改為 operator new[]
,也被叫為array new
。同樣array new也可以被過載,
2、array new
必須呼叫陣列中的每個物件的constructor
函數。上面那個例子就會呼叫10個A的無參建構函式。
3、array new
在釋放記憶體時。上面那個例子就會呼叫10個A的destructor
函數。
4、該類必須有無參建構函式。
所以我們同樣也可以修改operator new[]所呼叫的 new operator函數,以及delete[] operator。
在面對陣列時,new 會額外分配空間來儲存new的長度(一般為一個指標大小,32位元平臺下4位元組,64位元平臺下8位元組)。這個叫系統維護開銷。
下面是測試程式碼,類A是個空類只佔一個位元組,正常來說應該申請10個位元組的記憶體。
int main() { A* pa = new A[10]; delete[] pa; return 0; }
32位元環境下:
64位元環境下:
可以看到對申請了一個指標的記憶體用來存放申請物件的個數。
下面針對new的三種使用方式做了一個使用場景總結,切記操作對應的new 時還需要對應的delete。
1、需要將物件建立在堆區,那麼就使用 new operator
也就是new
操作符。它會幫你分配記憶體並呼叫constructor
函數。
2、僅需要分配記憶體,那麼就使用operator new
,這樣就不會呼叫constructor
函數。
3、需要在堆區建立物件時自定義記憶體分配方式,那麼就需要重寫operator new
函數然後使用new operator
即可。
4、需要在已分配的記憶體中呼叫建構函式,那麼就使用placement new
。
到此這篇關於淺談C++中各種不同意義的new和delete的使用的文章就介紹到這了,更多相關C++ new和delete 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45