前言 本篇來分析new是怎麼實現的, 使用c++進行在申請對象的時候用到new, 但是為什麼申請對象要用到new, 而不能用malloc, 而有時申請數組的用new或者malloc似乎又都可以, 這裡就來分析一下new實現. new operator, operator new以及placement ...
前言
本篇來分析new是怎麼實現的, 使用c++進行在申請對象的時候用到new, 但是為什麼申請對象要用到new, 而不能用malloc, 而有時申請數組的用new或者malloc似乎又都可以, 這裡就來分析一下new實現.
new operator, operator new以及placement new
-
new operator用法
其實new operator我們經常在使用, 就是我們直接向堆申請一塊記憶體大小, 然後對該記憶體進行構造和析構.
template<class T> class point { T x; T y; }; point<int> *p = new point<int>[3];
這就是new operator的用法. 其實在使用它的時候, 它會做兩步事情.
-
向堆申請一塊大小的記憶體.
-
對其有構造函數的執行構造函數
其實剩下的兩種用法就是將 new operator 的兩個功能分開做.
-
operator new用法
operator new申請一塊空間, 但是申請完了就什麼都不做. 這感覺就很像malloc
函數啊. 對, 沒錯. 其實operator new就是間接性的調用了 malloc函數. 我們直接來看源碼部分
1 void* __CRTDECL operator new(size_t const size) 2 { 3 for (;;) 4 { 5 if (void* const block = malloc(size)) 6 { 7 return block; 8 } 9 10 if (_callnewh(size) == 0) 11 { 12 if (size == SIZE_MAX) 13 { 14 __scrt_throw_std_bad_array_new_length(); 15 } 16 else 17 { 18 __scrt_throw_std_bad_alloc(); 19 } 20 } 21 } 22 }
很清楚的可以看出來5行的確是直接的調用了malloc函數, 然後除了申請的大小判斷就沒有了, 那為什麼我們不直接用malloc函數而要用operator new??? 主要是new的封裝, 可重載吧, 畢竟我們常說new不是函數, 而是操作符也是有原因的. 接下來就是最後一個了.
-
placement new用法
placement
new
是在已經申請的記憶體上構建對象. 這就是我們調用new的時候會調用對象的構造函數的原因. 有一點, 剛說了可以在已經申請的記憶體上構建對象, 難不成不只是堆, 連棧上也能構建對象.
這也是我們記憶體池經常用的方法, 使用placement new在已經申請的記憶體上構建對象
它的用法就很靈活了.
int buff[10]; int *p = new(buff) int(0);
這樣我們就在已分配空間的buff中重新構建對象了, 傳入的buff代表的是地址, 後面括弧代表的初始化的值. 這個例子也證實了我們可以在棧中分配對象, 因為buff就在棧中. 而buff[0]與p都指向的同一塊地址.
這裡就要註意, 我們用buff地址開始申請的對象, 就儘量不要用buff了, 因為buff的數據被重新的修改了, 使用buff可能就會出現奇怪的數據.
同時, buff的長度要足夠裝下對象的大小, 否則就會出現數據覆蓋的危險. 這個博主詳細的實現了這個問題 .
現在我們來驗證一下上面說的吧. 這個一個很簡單的程式, 通過gdb調試定位在new的執行, 可以看到
template<class T> class point { public: point() { } private: T x; }; int main() { point<int> *p = new point<int>[3]; return 0; }
圖片中的第二行, 對應的跳轉就是我們最後一行的要跳轉的操作符(operator new), 執行了之後在圖片中的下一個call就是調用類的構造函數. new操作符的實現是兩步就可概括為先申請了空間, 再調用構造函數.
new的重載實現
前面提了幾次關於new可以重載, 那怎麼實現重載呢? 我們不是說過new是一個運算符嗎, 就是重載運算符就行了.
template<class T>class point { public: point(T i) : i(i) { cout << "point constructor" << endl; } void *operator new(size_t size, void *p, const string& str) { cout << "operator new" << endl; if (!p) { cout << "new" << endl; return ::operator new(size); } return p; } private: T i; }; char buf[sizeof(point<int>)]; point<int> *pc = new (buf, "first new") point<int>(1);
以上就是簡單的new的重載. 運行時會有一個奇怪的現象, new先執行, 原因就是先要為類分配記憶體啊, 所以new比構造函數先執行.
總結
解釋了new, 同樣的, delete的實現也是跟new有很多相似的, delete事先調用析構函數, 然後再調用free函數釋放記憶體, 同樣是可以將析構和釋放記憶體分開調用, 也可以進行重載, 這裡就不細講了. 至於為什麼new的對象一定要用delete來釋放也容易想明白, 因為delete會預設調用其析構函數, 而free僅僅只是釋放空間而沒有調用析構. 如果是普通變數用new或malloc申請記憶體都是可以用delete釋放, 畢竟沒有析構的就預設什麼也不做然後釋放記憶體.
參考 :