C++中的std::allocator

C++中的std::allocator

文章目錄

  • C++中的std::allocator
    • 1.`std::allocator`
      • 1.1C++中的placement new 和`operator new`
      • 1.2一個custom allocator的實現
      • 1.3使用`std::allocator_traits`實現allocator

1.std::allocator

C++中的std::allocator默默工作在C++STL中的所有容器的內存分配上,很多內存池是按照std::allocator的標準來實現的,甚至很多開源的內存儲項目可以和大多數STL容器兼容,在很多場景下,內存池是std::allocator的優化。

在C++中,傳統new操作符將內存分配(operator new,這里的operator new是C++的內存分配原語,默認調用C語言中的malloc,只是進行內存分配)和對象構造(構造函數)耦合。即new運算符需要同時完成內存分配和對象構造兩個操作。

std::allocator將解耦內存分配和對象構造這兩個操作,按照C++11的標準,實現一個std::allocator需要包含以下的元素和方法

  • value_type:將模板的參數類型T定義為value_type,如using value_type = T;或者typedef T value_type;
  • allocate():僅分配原始內存,功能就類似opeartor new
  • construct():在預分配的內存上構造對象(通過使用C++中的placement new機制)
  • destroy():析構對象但不釋放內存
  • deallocate():釋放原始內存(類似于operator delete

注釋: https://cplusplus.com/reference/memory/allocator/

1.1C++中的placement new 和operator new

placement new 是C++中一種特使的內存分配的對象構造機制,它允許在已分配的內存上直接構造對象,而不是通過傳統的new操作符同時分配內存和構造對象。

placement new的語法形式為:

new (pointer) Type(constructor_arguments);

其中:

  • pointer是指向已分配內存的指針
  • Type是要構造的對象
  • constructor_arguments是構造函數的參數

placement new的工作原理是,不調用operator new來分配內存,而是在給定的內存地址上直接調用構造函數,最后返回傳入的指針(將指針類型轉換為目標類型)。placement new由C++標準庫提供默認實現,不可重載:

// 標準庫中的 placement new 聲明(不可重載)
void* operator new(size_t, void* ptr) noexcept {return ptr;  // 直接返回傳入的指針
}

乍一看,這個placement new的實現什么都沒干,是如何完成對象的構造呢?其實是依靠語法來進行創建的:

new (pointer) Type(constructor_arguments);

這里仍然調用了Type(constructor_arguments),即調用了對象的構造函數,在pointer指定的內存上進行構造,舉個例子:

#include <iostream>
struct Example {int value;Example(int val) : value(val) {std::cout << "Constructed at " << this << " with value " << value << std::endl;}~Example() {std::cout << "Destructed at " << this << std::endl;}
};
int main() {// 手動分配一塊內存void* buffer = operator new(sizeof(Example));// 使用placement new在這塊內存上構造對象Example* obj = new (buffer) Example(42);// 顯式調用析構函數(這很重要!)obj->~Example();// 釋放內存operator delete(buffer);return 0;
}

輸出為:

Constructed at 0x7fec4c400030 with value 42
Destructed at 0x7fec4c400030

operator new是C++的內存分配原語,默認調用malloc進行內存分配,返回void*,指向未初始化的原始內存,可以重載operator new以自定義其內存分配行為:

// 自定義全局 operator new
void* operator new(size_t size){std::cout << "Allocating " << size << " bytes\n";return malloc(size);
}

使用opeartor new和placement new的典型場景如下:

// 僅分配內存,不構造對象
void* raw_mem = operator new(sizeof(MyClass));// 需要手動構造對象(例如通過 placement new)
MyClass* obj = new (raw_mem) MyClass();  // 調用構造函數// 必須手動析構和釋放
obj->~MyClass();
operator delete(raw_mem);

核心區別

特性operator newplacement new
作用僅分配原始內存(不構造對象)在已分配的內存上構造對象
是否調用構造函數
內存來源通常來自于堆(可通過重載自定義)由程序員預先提供
語法void* p = operator new(size)new (ptr) Type(args...)
是否可重載可重載全局或類特定的operator new不能重載,已經有固定實現

1.2一個custom allocator的實現

一個自定義的allocator需要實現以下的方法:

方法描述等效操作
allocate(n)分配n* sizeof(T)字節operator new
deallocate(p, n)釋放從p開始的n個元素operator delete
construct(p, args)p構造對象(C++17已棄用)new(p) T(args...)
destroy(p)析構p處對象(C++17已棄用)p->~T()

注釋:C++17 后推薦通過 std::allocator_traits 訪問接口,以支持自定義分配器的可選方法。

按照C++11的標準實現一個allocator

#include <iostream>
#include <vector>
template<typename T>
class TrackingAllocator {
public:using value_type = T;TrackingAllocator() = default;// 支持 Rebinding(重新綁定)template<typename U>TrackingAllocator(const TrackingAllocator<U>&) {}T* allocate(size_t n) {size_t bytes = n * sizeof(T);std::cout << "Allocating " << bytes << " bytes\n";return static_cast<T*>(::operator new(bytes));}void deallocate(T* p, size_t n) {::operator delete(p);std::cout << "Deallocating " << n * sizeof(T) << " bytes\n";}// 支持同類型分配器比較(無狀態)bool operator==(const TrackingAllocator&) { return true; }bool operator!=(const TrackingAllocator&) { return false; }
};// 使用示例
int main() {// 使用自定義分配器std::vector<int, TrackingAllocator<int>> vec;vec.push_back(42);  // 輸出分配信息vec.push_back(13);  // 輸出分配信息// 清空向量vec.clear();  // 輸出釋放信息return 0;
}

輸出:

Allocating 4 bytes
Allocating 8 bytes
Deallocating 4 bytes
Deallocating 8 bytes

1.3使用std::allocator_traits實現allocator

在 C++17 及之后版本中,推薦通過 std::allocator_traits 訪問分配器接口,而非直接調用分配器的方法。這是因為 allocator_traits 提供了一種統一且安全的方式來與分配器交互,即使自定義分配器沒有實現某些可選方法,也能通過默認實現正常工作。

  • 兼容性:即使自定義分配器未實現某些方法(如 construct/destroy),allocator_traits 會提供默認實現。
  • 靈活性:允許分配器僅實現必要的接口,其余由 allocator_traits 補充。
  • 標準化:所有標準庫容器(如 std::vectorstd::list)內部都使用 allocator_traits 而非直接調用分配器。

注釋:https://cplusplus.com/reference/memory/allocator_traits/

關鍵接口對比(使用C++11標準 vs. C++17標準)

操作C++11,直接調用分配器allocC++17,通過allocator_traits(std::allocator_traits<Alloc>)
分配內存alloc.allocate(n)allocator_traits<Alloc>::allocate(alloc, n)
釋放內存alloc.deallocate(p, n)allocator_traits<Alloc>::deallocate(alloc, p, n)
構造對象alloc.construct(p, args)allocator_traits<Alloc>::construct(alloc, p, args...)
析構對象alloc.destroy(p)allocator_traits<Alloc>::destroy(alloc, p)
獲取最大大小alloc.max_size()allocator_traits<Alloc>::max_size(alloc)
重新綁定分配器類型alloc.rebind<U>::otherallocator_traits<Alloc>::rebind_alloc<U>

注釋:C++17 后 constructdestroy 被廢棄,推薦直接使用 std::allocator_traits 或 placement new/顯式析構。

舉個極簡分配器的例子:

#include <iostream>
#include <memory>  // std::allocator_traitstemplate <typename T>
struct SimpleAllocator {using value_type = T;// 必須提供 allocate 和 deallocateT* allocate(size_t n) {return static_cast<T*>(::operator new(n * sizeof(T)));}void deallocate(T* p, size_t n) {::operator delete(p);}// 不提供 construct/destroy,由 allocator_traits 提供默認實現
};struct Widget {int id;Widget(int i) : id(i) { std::cout << "Construct Widget " << id << "\n"; }~Widget() { std::cout << "Destroy Widget " << id << "\n"; }
};int main() {using Alloc = SimpleAllocator<Widget>;Alloc alloc;// 1. 分配內存(通過 allocator_traits)auto p = std::allocator_traits<Alloc>::allocate(alloc, 1);// 2. 構造對象(即使 SimpleAllocator 沒有 construct 方法!)std::allocator_traits<Alloc>::construct(alloc, p, 42);  // 調用 Widget(42)// 3. 析構對象(即使 SimpleAllocator 沒有 destroy 方法!)std::allocator_traits<Alloc>::destroy(alloc, p);// 4. 釋放內存std::allocator_traits<Alloc>::deallocate(alloc, p, 1);return 0;
}

輸出:

Construct Widget 42
Destroy Widget 42

一個更復雜的自定義分配器示例(帶狀態)

#include <iostream>
#include <memory>  // std::allocator_traitstemplate <typename T>
class TrackingAllocator {size_t total_allocated = 0;
public:using value_type = T;T* allocate(size_t n) {total_allocated += n * sizeof(T);std::cout << "Allocated " << n * sizeof(T) << " bytes (Total: " << total_allocated << ")\n";return static_cast<T*>(::operator new(n * sizeof(T)));}void deallocate(T* p, size_t n) {total_allocated -= n * sizeof(T);std::cout << "Deallocated " << n * sizeof(T) << " bytes (Remaining: " << total_allocated << ")\n";::operator delete(p);}// 支持比較(相同類型的 TrackingAllocator 才等價)bool operator==(const TrackingAllocator& other) const {return false;  // 有狀態,不同實例不能混用}bool operator!=(const TrackingAllocator& other) const {return true;}
};int main() {using Alloc = TrackingAllocator<int>;Alloc alloc1, alloc2;auto p1 = std::allocator_traits<Alloc>::allocate(alloc1, 2);auto p2 = std::allocator_traits<Alloc>::allocate(alloc2, 3);// 必須用相同的 allocator 實例釋放!std::allocator_traits<Alloc>::deallocate(alloc1, p1, 2);std::allocator_traits<Alloc>::deallocate(alloc2, p2, 3);return 0;
}

輸出:

Allocated 8 bytes (Total: 8)
Allocated 12 bytes (Total: 12)
Deallocated 8 bytes (Remaining: 0)
Deallocated 12 bytes (Remaining: 0)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81159.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81159.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81159.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CodeBuddy編程新范式

不會寫&#xff1f;不想寫&#xff1f; 騰訊推出的CodeBuddy徹底解放雙手。 示例 以下是我對CodeBuddy的一個小體驗。 我只用一行文字對CodeBuddy說明了一下我的需求&#xff0c;剩下的全部就交給了CodeBuddy&#xff0c;我需要做的就是驗收結果即可。 1.首先CodeBuddy會對任…

QML學習01(設置寬度、高度、坐標點、標題,信號與槽,鍵盤事件)

QML學習 1、前言2、QML3、QML和QWidget的區別3、QtQuick下的Windows應用4、總結 1、前言 記錄一下QML學習的過程&#xff0c;方便自己日后回顧&#xff0c;也可以給有需要的人提供幫助。 2、QML QML是 Qt 框架中的一種聲明式編程語言&#xff0c;專門用于快速設計和開發用戶…

在VSCode中接入DeepSeek的指南

本文將介紹三種主流接入方式,涵蓋本地模型調用和云端API接入方案。 一、環境準備 1.1 基礎要求 VSCode 1.80+Node.js 16.x+Python 3.8+(本地部署場景)已部署的DeepSeek服務(本地或云端)1.2 安裝必備插件 # 打開VSCode插件面板(Ctrl+Shift+X) 搜索并安裝: - DeepSeek Of…

機器學習-計量經濟學

機器學習 不要事前決定變量關系&#xff0c;關鍵是誰也不知道啊&#xff0c;機器學習學習的模型&#xff08;那也不是真實的關系啊&#xff09; 這就是自然學科的好處&#xff1a;只要不斷的優化這個未知的東西&#xff08;函數&#xff09;&#xff0c;然后在數據上&#xff…

五、Linux賬號與權限管理

1、管理用戶和組賬號 1.1、用戶 1.1.1、用戶的概念及作用 在Linux系統中,用戶(User)指的是可以訪問系統資源的個體實體。每個用戶都有一個唯一的用戶賬號,用于標識和管理其在系統中的活動和訪問權限。 用戶的重要性和功能: 身份認證和訪問控制: 用戶賬號用于身份認證,確…

精益數據分析(61/126):移情階段評分體系構建與實戰案例解析

精益數據分析&#xff08;61/126&#xff09;&#xff1a;移情階段評分體系構建與實戰案例解析 在創業的移情階段&#xff0c;如何科學評估用戶需求的真實性與緊迫性&#xff0c;是決定后續產品方向的關鍵。今天&#xff0c;我們結合《精益數據分析》中的評分框架&#xff0c;…

完成反射宇宙的最后一塊拼圖:泛型集合

反射,c#的黑科技,一手打造漂亮的,專屬于自己的屬性框 之前分享的: 如何寫一個自定義屬性控件的功能,但是只是對基礎的類型,比如String,bool,int等,但是對list<T>,Vector<T>這種泛型集合類型支持的不是很好,剛好最近重新研究了一下,將這個非常重要的功能完成了. 效…

Redis--基礎知識點--26--過期刪除策略 與 淘汰策略

Redis 的過期策略和淘汰策略是內存管理的核心機制&#xff0c;分別用于處理鍵的自動失效和內存不足時的數據清理。以下是詳細說明&#xff1a; 1 、過期刪除策略&#xff08;Expiration Policy&#xff09; 處理已設置過期時間&#xff08;EXPIRE&#xff09;的鍵&#xff0c;…

第六天——貪心算法——字符串分隔

1. 題目 給定一個字符串 s&#xff0c;我們需要將其劃分為盡可能多的部分&#xff0c;使得同一字母最多出現在一個部分中。 例如&#xff1a;字符串 "ababcc" 可以劃分為 ["abab", "cc"]&#xff0c;但要避免 ["aba", "bcc&quo…

[原創](現代Delphi 12指南):[macOS 64bit App開發]: 注意“回車換行“的跨平臺使用.

[作者] 常用網名: 豬頭三 出生日期: 1981.XX.XX 企鵝交流: 643439947 個人網站: 80x86匯編小站 編程生涯: 2001年~至今[共24年] 職業生涯: 22年 開發語言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 開發工具: Visual Studio、Delphi、XCode、…

Maven 插件參數注入與Mojo開發詳解

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

擴增子分析|R分析之微生物生態網絡穩定性評估之節點和連接的恒常性、節點持久性以及組成穩定性指數計算

一、引言 周集中老師團隊于2021年在Nature climate change發表的文章&#xff0c;闡述了網絡穩定性評估的原理算法&#xff0c;并提供了完整的代碼。自此對微生物生態網絡的評估具有更全面的指標&#xff0c;自此網絡穩定性的評估廣受大家歡迎。本文將介紹網絡穩定性之節點和連…

人體肢體渲染-一步幾個腳印從頭設計數字生命——仙盟創夢IDE

人體肢體動作數據集-太極拳 渲染代碼 # 初始化Pygame pygame.init()# 設置窗口尺寸 WINDOW_WIDTH 800 WINDOW_HEIGHT 600 window pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("動作回放")# 設置幀率 FPS 30 clock pyg…

強化學習入門:馬爾科夫獎勵過程

文章目錄 前言1、組成部分2、應用例子3、馬爾科夫獎勵過程總結 前言 最近想開一個關于強化學習專欄&#xff0c;因為DeepSeek-R1很火&#xff0c;但本人對于LLM連門都沒入。因此&#xff0c;只是記錄一些類似的讀書筆記&#xff0c;內容不深&#xff0c;大多數只是一些概念的東…

騰訊開源實時語音大模型VITA-audio,92mstoken極速響應,支持多語言~

簡介 VITA-Audio 是一個由騰訊優圖實驗室&#xff08;Tencent Youtu Lab&#xff09;、南京大學和廈門大學的研究人員共同開發的項目&#xff0c;旨在解決現有語音模型在流式生成&#xff08;streaming&#xff09;場景下生成第一個音頻令牌&#xff08;token&#xff09;時的高…

測序的原理

Sanger 測序原理 https://v.qq.com/x/page/d0124c0k44t.html illumina 測序原理&#xff1a; https://v.qq.com/x/page/i0770fd7r9i.html PacBio 第三代 SMRT 單分子測序 https://v.qq.com/x/page/r03534cry7u.html Ion torrent 測序原理 https://v.qq.com/x/page/v01754s6r82.…

高項-邏輯數據模型

邏輯數據模型的核心理解 1. 定義與特點 邏輯數據模型&#xff08;Logical Data Model, LDM&#xff09;&#xff1a; 是一種抽象的數據結構設計&#xff0c;用于描述業務實體&#xff08;如客戶、訂單&#xff09;及其關系&#xff08;如“客戶下單”&#xff09;&#xff0c…

《數字分身進化論:React Native與Flutter如何打造沉浸式虛擬形象編輯》

React Native&#xff0c;依托JavaScript語言&#xff0c;借助其成熟的React生態系統&#xff0c;開發者能夠快速上手&#xff0c;將前端開發的經驗巧妙運用到移動應用開發中。它通過JavaScript橋接機制調用原生組件&#xff0c;實現與iOS和Android系統的深度交互&#xff0c;這…

提高繩牽引并聯連續體機器人運動學建模精度的基于Transformer的分段學習方法

合肥工業大學王正雨老師團隊針對繩牽引并聯連續體機器人的運動學建模提出一種基于Transformer網絡的分段學習方法&#xff0c;該方法較傳統建模性能卓越、精度更高。相關研究論文“Transformer-based segmented learning for kinematics modelling of a cable-driven parallel …

【PX4飛控】在 Matlab Simulink 中使用 Mavlink 協議與 PX4 飛行器進行交互

這里列舉一些從官網收集的比較有趣或者實用的功能。 編寫 m 腳本與飛行器建立 UDP 連接&#xff0c;并實時可視化 Mavlink 消息內容&#xff0c;或者讀取腳本離線分析數據。不光能顯示 GPS 位置或者姿態等信息的時間曲線&#xff0c;可以利用 Matlab Plot 功能快速定制化顯示一…