c++ 中new 操作符是怎么实现的
认证高级PHP讲师
首先要區分 new operator 和 operator new 這個兩個概念。
new operator
operator new
前者就是題中的 new 操作符。
new 操作符
一般,在 new 一個對象的時候,底層做了一下兩步:
為對象分配內存;
調用構造函數初始化這塊內存;
在第一步中,分配內存就使用了 operator new 。 @肥夏 的第二個例子就是一個 operator new 。在第二步中,調用構造函數初始化內存。如果不涉及繼承,這一步非常簡單,就是給內存賦值。即使有繼承關係的話,也是複雜在對繼承關係、虛函數表的梳理上,與本題關係不大。
下麵重點說說第一步中的 operator new,它職責就是分配內存。
operator new 的一般處理流程正如 @肥夏 的第二個例子所給出的:
分配內存,如果成功則直接返回,否則,
查看 new_handler 是否可用,如果可用則調用之並跳轉到 1 ,否則,
new_handler
拋出 bad_alloc 異常。
bad_alloc
上麵的 new_handler 會嚐試釋放一部分內存。
了解這個流程之後,我們再延伸討論一下另外兩個問題。
如下代碼片段中的空檢查是否有意義?
Question * q = new Question; if (q == NULL ) { // 错误处理逻辑 }
從上麵我們了解到,operator new 在分配內存失敗的情況下會調用 new_handler 嚐試讓係統釋放點內存,然後再次嚐試申請內存。如果這時係統中內存確實緊張,即使調用 new_handler ,係統也拿不出更多的內存,這就會導致 operator new 陷入一個死循環。
總而言之,operator new 要麼成功申請到內存,要麼在那死循環不出來。
然後,再考慮 new operator 的第二步,調用構造函數初始化內存。眾所周知,C 的構造函數是不返回任何返回值的,如果想讓構造函數失敗隻能是拋出異常。
由以上兩點我們可以得出結論:new Question 是永遠不會返回空指針的。
new Question
進一步地,new Question 失敗隻有兩種情況:
operator new 拋出 bad_alloc 異常;
構造函數拋出自己的異常。
所以,上麵代碼片段中的判空邏輯是否有意義呢?是否使用 try ... catch 來捕獲 new 的異常才是更好的判斷 new 失敗的方法呢?
try ... catch
new
自定義內存管理
一般打 new 的主意的都是想實現自己的內存管理(比如使用內存池),並且想在不改變現有代碼的基礎上讓舊代碼也能享受到新的內存管理所帶來的福利。
具體做法 @肥夏 所引用的第二篇文章裏已經講的很清楚。但因為重載 new operator 對整個係統的影響可能遠比最初的想象大得多,因此可以考慮以下兩個折中的方案:
使用 placement new;
placement new
實現一個新的 New (而不是重載原來的 operator new),然後在確實需要的地方使用。
new是C 的語言特征, 不是函數.
它可能會調用malloc, 但是具體如何分配內存要取決於實現. 相比於malloc, new默認在申請失敗的時候會拋出異常而不是直接返回0.
和new對應的是delete, 它們必須成對出現. new[] 則要和 delete[] 同時出現.
new是C 關鍵字,你可以嚐試自己去實現一個new ,必要的時候利用自己的實現的函數去替換默認的new做一些檢查之類的事情。 比如如下鏈接內的寫法cppreference鏈接
void* operator new(std::size_t sz) { return std::malloc(sz); }
複雜點的實現——深入探究C 的new/delete操作符 - 作者@Kelvin
BLOG鏈接,侵刪
void * operator new(std::size_t size) throw(std::bad_alloc) { if (size == 0) size = 1; void* p; while ((p = ::malloc(size)) == 0) { std::new_handler nh = std::get_new_handler(); if (nh) nh(); else throw std::bad_alloc(); } return p; } void operator delete(void* ptr) { if (ptr) ::free(ptr); }
多了用以處理內存不足的函數new_handler
首先要區分
new operator
和operator new
這個兩個概念。前者就是題中的
new 操作符
。一般,在 new 一個對象的時候,底層做了一下兩步:
為對象分配內存;
調用構造函數初始化這塊內存;
在第一步中,分配內存就使用了
operator new
。 @肥夏 的第二個例子就是一個operator new
。在第二步中,調用構造函數初始化內存。如果不涉及繼承,這一步非常簡單,就是給內存賦值。即使有繼承關係的話,也是複雜在對繼承關係、虛函數表的梳理上,與本題關係不大。
下麵重點說說第一步中的
operator new
,它職責就是分配內存。operator new
的一般處理流程正如 @肥夏 的第二個例子所給出的:分配內存,如果成功則直接返回,否則,
查看
new_handler
是否可用,如果可用則調用之並跳轉到 1 ,否則,拋出
bad_alloc
異常。上麵的
new_handler
會嚐試釋放一部分內存。了解這個流程之後,我們再延伸討論一下另外兩個問題。
如下代碼片段中的空檢查是否有意義?
從上麵我們了解到,
operator new
在分配內存失敗的情況下會調用new_handler
嚐試讓係統釋放點內存,然後再次嚐試申請內存。如果這時係統中內存確實緊張,即使調用new_handler
,係統也拿不出更多的內存,這就會導致operator new
陷入一個死循環。總而言之,
operator new
要麼成功申請到內存,要麼在那死循環不出來。然後,再考慮
new operator
的第二步,調用構造函數初始化內存。眾所周知,C 的構造函數是不返回任何返回值的,如果想讓構造函數失敗隻能是拋出異常。由以上兩點我們可以得出結論:
new Question
是永遠不會返回空指針的。進一步地,
new Question
失敗隻有兩種情況:operator new
拋出bad_alloc
異常;構造函數拋出自己的異常。
所以,上麵代碼片段中的判空邏輯是否有意義呢?是否使用
try ... catch
來捕獲new
的異常才是更好的判斷new
失敗的方法呢?自定義內存管理
一般打 new 的主意的都是想實現自己的內存管理(比如使用內存池),並且想在不改變現有代碼的基礎上讓舊代碼也能享受到新的內存管理所帶來的福利。
具體做法 @肥夏 所引用的第二篇文章裏已經講的很清楚。但因為重載
new operator
對整個係統的影響可能遠比最初的想象大得多,因此可以考慮以下兩個折中的方案:使用
placement new
;實現一個新的 New (而不是重載原來的
operator new
),然後在確實需要的地方使用。new是C 的語言特征, 不是函數.
它可能會調用malloc, 但是具體如何分配內存要取決於實現. 相比於malloc, new默認在申請失敗的時候會拋出異常而不是直接返回0.
和new對應的是delete, 它們必須成對出現. new[] 則要和 delete[] 同時出現.
new是C 關鍵字,你可以嚐試自己去實現一個new ,必要的時候利用自己的實現的函數去替換默認的new做一些檢查之類的事情。
比如如下鏈接內的寫法
cppreference鏈接
複雜點的實現——深入探究C 的new/delete操作符 - 作者@Kelvin
BLOG鏈接,侵刪
多了用以處理內存不足的函數new_handler