系統程式設計師的 C++

Allocator 概念與 std::pmr(多型配置器)

預設情況下,std::vector 或 std::map 每次需要記憶體時都向全域堆積(operator new,最終是 malloc())索取。對大多數程式這沒問題,但在系統程式設計裡,你有時需要容器改從某個特定來源取得記憶體——一個預留的競技場(arena)、堆疊上的固定緩衝區、為單一請求量身的池——以控制碎片、延遲或區域性。C++ 讓你能藉由提供一個自訂配置器來做到這點,容器會改用它而非全域堆積。

配置器是個知道如何發放與回收給定型別之裸記憶體的物件;標準函式庫(非正式地)把 Allocator 概念定義為容器所期望的一組操作——主要是 allocate(n) 取得能容納 n 個物件的儲存空間,以及 deallocate(p, n) 把它還回去,外加型別資訊。經典的模板參數做法(std::vector<int, MyAlloc>)把配置器烤進容器的型別,這很快卻僵硬。C++17 加入 std::pmr(多型記憶體資源,polymorphic memory resources)來解決這點:std::pmr::memory_resource 是個有虛擬 do_allocate/do_deallocate 的基底類別,而 std::pmr 容器持有指向某個它的指標。如今配置策略是在執行期透過虛擬呼叫選定,所以一個在堆疊陣列上用 monotonic_buffer_resource 的 std::pmr::vector<int> 可以傳給普通程式碼,配置器不會感染它的型別。

為何重要:自訂配置器讓你把許多次小的全域堆積呼叫,替換成一次來自競技場的廉價遞進指標配置,在熱路徑上消除每次配置的開銷與碎片——這對延遲敏感的系統是真正的槓桿。誠實的代價:經典的 Allocator 概念以難以手工實作著稱(這正是 pmr 存在的原因),pmr 的虛擬分派重新引入了每次配置的小幅間接(為了彈性而刻意做的取捨),而你仍需為底層記憶體資源的生存期負責——容器絕不可活得比它的配置器所取用的緩衝區或競技場更久。

std::byte buf[4096]; std::pmr::monotonic_buffer_resource r{buf, sizeof buf}; std::pmr::vector<int> v{&r}; // v 從 buf 配置,而非全域堆積

vector 透過多型記憶體資源從一個 4 KiB 的堆疊緩衝區取得所有儲存空間——這條路徑上沒有 malloc() 呼叫。

pmr 容器持有指向其 memory_resource 的非擁有指標,所以該資源(以及它所包裝的任何緩衝區)必須活得比容器久。若在 vector 仍存活時讓緩衝區離開範圍,你就有了懸置的配置器——一個蓄勢待發的釋放後使用。

又称
custom allocatorspolymorphic memory resources自訂配置器多型記憶體資源