第八章 —— Customizing new and delete 看似簡單,但其中蘊含了許多名詞容易混淆。就讓我們用幾道小測驗來釐清觀念、鞏固學習成果吧!
Q1. set_new_handler
的主要用途是什麼?
A) 設定處理程式來處理程式中所有的 exception。
B) 指定一個函式,在建構子丟出 exception 時呼叫。
C) 設定處理 delete
操作失敗的情況。
D) 自訂全域 delete
的行為。
E) 在 new
無法滿足記憶體配置請求時呼叫。
set_new_handler
是 標頭檔中的函式,可用來指定一個稱為new-handler
的函式。當new
無法成功分配所要求的記憶體時,這個函式會被呼叫。
為了符合 C++ 規範,即使是自定義的 new
也要在擲出 std::bad_alloc
前先處理記憶體不足的情況,例如釋放資源、記錄錯誤等。
Q2.假設某個 C++ 程式頻繁地配置與釋放大量小型物件,下列哪一種方法最能夠顯著提升效能?
A) 完全依賴編譯器提供的通用配置器。
B) 使用自訂配置器或專用函式庫。
C) 完全避免使用動態記憶體配置。
D) 所有物件都使用全域變數。
E) 將所有物件寫入磁碟而非配置在記憶體中。
在處理大量小型物件頻繁配置與釋放時,使用通用記憶體配置器可能造成效能瓶頸。自訂配置器或像 Boost.Pool 這樣針對小型物件優化的 memory pool 能顯著提升效能,因為它們能減少碎片與配置開銷。
關於 Boost 的 Pool library 會在知識點 55 詳細介紹。其不只可以管理大量的對象,亦能作為 STL 的內存分配器,甚至可以無需考慮到 delete
。自訂配置器則對於重視效能的場景尤為重要,例如在容器中頻繁處理小型物件時。
*Q3. 當你實作自訂非成員的 new
時,哪一項是符合慣例的必要做法?
A) 當記憶體無法分配時總是回傳 nullptr
。
B) 僅在一次分配失敗後才擲出 std::bad_alloc
。
C) 將請求 0 位元組視為請求 1 個位元組。
D) 僅當請求的大小大於 0 時才回傳指標。
E) 僅當指標不是 null
時才呼叫 new-handler
函式。
C++ 標準要求
new
即使在請求 0 位元組時,也必須回傳一個合法的非null
的指標。通常這種請求會被當作是 1 個位元組的配置處理,這樣可以簡化語言的記憶體模型與用法一致性。
nullptr
是 C++11 引入的新關鍵字,用以取代舊式的 null
。它是一個具備明確型別的空指標常數,適用於所有指標型別,包括函式指標、物件指標等。不會產生型別歧義。
Q4.關於 new
與 delete
,哪一個敘述是錯誤的?
A) Placement new 指的是任何除了 std::size_t 以外還有其他參數的 operator new。
B) Placement delete 指的是與 placement new 擁有相同額外參數的 operator delete。
C) 標準的 placement new 由 C++ 標準函式庫提供,帶有 void*
參數。
D) 使用 placement new 建立的物件,在使用 delete 時會自動呼叫對應的 placement delete。
E) 如果宣告了 placement new,應該也宣告對應的 placement delete。
使用 placement new 建立的物件,不應直接使用
delete
。因為 placement new 是在指定的位置上建構物件,delete 不會知道這個物件是在已存在的記憶體中建構的,因此不會也不應呼叫對應的 placement delete。placement delete 只會在建構子丟出 exception 時才被呼叫。
自訂 placement new,就應該提供對應的 placement delete,例如手動呼叫解構子並回收記憶體:
#include <new>
#include <iostream>
struct Yoyo { Yoyo() { std::cout << "Hi from Yoyo!\n"; } };
int main() {
alignas(Yoyo) char buf[sizeof(Yoyo)];
Yoyo* y = new (buf) Yoyo(); // placement new here...
y->~Yoyo(); // placement delete here...
}