当前位置: 首页 > news >正文

C++ STL——allocator

C++ 标准模板库提供了一个非常重要的内存管理机制——allocator。它对内存管理接口进行了封装,实现了对象内存分配与构造分离、对象析构与内存释放分离。 标准库中通过模板参数为容器提供内存管理策略,allocator 就是一种具体的策略,我们可以实现基于共享内存、内存池的 自定义 allocator 把它们应用于容器。本文章将以 MSVC 的 allocator 框架为参考,讲述 allocator 的核心工作原理。

1. allocator_traits

allocator_traits,它是 C++11 开始引入的“萃取器”,主要用于“萃取” allocator 的类型,并且 C++11 之后 rebind_alloc 也被移入到 allocator_traits 中。MSVC 中 allocator_traits 的实现方式是:为std::allocator 的 traits 继承 _Default_allocator_traits,如下代码片段:

struct _Default_allocator_traits 
{  // [0.0] “萃取” allocator 类型、元素类型using allocator_type = _Alloc;using value_type     = typename _Alloc::value_type;// [0.1] rebind_alloc template<class _Other>using rebind_alloc = std::allocator<_Other>; 
};template<class _Alloc>
struct allocator_traits : _Default_allocator_traits<_Alloc> {};

2. rebind_alloc

rebind_alloc, 因为list等容器的元素类型与结点的类型并不一致,所以要单独为结点类型对象内存分配、构造等目的提供一个allocator,其命名含义不必细究。

// 使用 alias template 定义了泛型的别名
template<class _Alloc, class _Value_type>
using _Rebind_alloc_t = typename allocator_traits<_Alloc>::template rebind_alloc<_Value_type>;

3. 使用 allocator

allocator 主要作为容器的模板参数使用,如下 list 模板的第二个模板参数。

// [3.0] 结点类型
template <class _Value_type>
struct _List_node { // list nodeusing value_type = _Value_type;void* _Next; // successor node, or first element if headvoid* _Prev; // predecessor node, or last element if head_Value_type _Myval; // the stored value, unused if head
};template<class _Ty, class _Alloc = std::allocator<_Ty>>
class list {
public:// [3.0] list node template instanceusing _Node          = _List_node<_Ty>;// [3.1] 定义了结点类型对应的 allocatorusing _Alnode        = _Rebind_alloc_t<_Alloc, _Node>;// [3.4] list<int>, _Alnode <==> std::allocator<_List_node<int>>;_Compressed_pair<_Alnode, _Scary_val> _Mypair;public:// [3.2] 向容器(假设是list<int>类型)添加元素void push_back(const _Ty& _Val) {_Emplace(_Mypair._Myval2._Myhead, _Val);}template <class... _Valty>_Nodeptr _Emplace(const _Nodeptr _Where, _Valty&&... _Val) {// list<int>, _Alnode <==> std::allocator<_List_node<int>>// _Getal() <==> std::allocator<_List_node<int>>// [3.3] 这里没有使用直接使用allocator接口,而是借助一个类对象_List_node_emplace_op2<_Alnode> _Op{_Getal(), forward<_Valty>(_Val)...};}// [3.4] 数据成员中有list的结点对象分配器“实例”,该函数返回它的引用//  list<int>, _Alnode <==> std::allocator<_List_node<int>>;_Alnode& _Getal() noexcept {return _Mypair._Get_first();}
};

4. 容器结点分配器类

该类对象作为容器类数据成员,提供 allocator 相关能力

// store a pair of values, deriving from empty first
template <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> && !is_final_v<_Ty1>>
class _Compressed_pair final : private _Ty1 { 
public:_Ty2 _Myval2;//  list<int>, _Ty1 <==> std::allocator<_List_node<int>>constexpr _Ty1& _Get_first() noexcept {return *this;}
};

5.构造行为封装

上文提到向容器中添加元素(在某个位置构造)操作由该类进行封装

// [5.0] 父类
template <class _Alloc>
struct _Alloc_construct_ptr {_Alloc& _Al;explicit _Alloc_construct_ptr(_Alloc& _Al_) : _Al(_Al_) {}
};// [5.0] 子类
template <class _Alnode>
struct _List_node_emplace_op2 : _Alloc_construct_ptr<_Alnode>  {// list<int>, _Alnode <==> std::allocator<_List_node<int>>,// allocator_traits<_Alnode> <==> allocator_traits<std::allocator<_List_node<int>>>// allocator_traits::allocator_type <==> std::allocator<_List_node<int>>// [5.1] 结点 allocator 的“萃取器”using _Alnode_traits = allocator_traits<_Alnode>;using pointer        = typename _Alnode_traits::pointer;// [5.1] 注意构造该类对象时引用了容器的 allocatortemplate <class... _Valtys>explicit _List_node_emplace_op2(_Alnode& _Al_, _Valtys&&... _Vals) : _Alloc_construct_ptr<_Alnode>(_Al_) {this->_Allocate();// allocator_traits<std::allocator<_List_node<int>>>::construct(//  this->_Al, addressof(this->_Ptr->_Myval),//  std::forward<_Valtys>(_Vals)...);// [5.2] 结点 allocator 的“萃取器”的静态成员函数完成元素构造_Alnode_traits::construct(this->_Al, addressof(this->_Ptr->_Myval),std::forward<_Valtys>(_Vals)...);}
};

6. 封装 allocate、deallocate、construct、destroy

这个类型看着比较面熟,没错前面只列出它的类型别名,现在为它添加核心函数

template<class _Alloc>
struct _Default_allocator_traits 
{  _NODISCARD static _CONSTEXPR20_DYNALLOC __declspec(allocator) pointerallocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count) {return _Al.allocate(_Count);}_NODISCARD static _CONSTEXPR20_DYNALLOC __declspec(allocator) pointerallocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count, const const_void_pointer _Hint) {if constexpr (_Has_allocate_hint<_Alloc, size_type, const_void_pointer>::value) {return _Al.allocate(_Count, _Hint);} else {return _Al.allocate(_Count);}}static _CONSTEXPR20_DYNALLOC void deallocate(_Alloc& _Al, pointer _Ptr, size_type _Count) {_Al.deallocate(_Ptr, _Count);}// [6.0] 实现构造行为,可能使用 placement new实现,也可能是其它实现,具体看c++库版本template <class _Ty, class... _Types>static _CONSTEXPR20_DYNALLOC void construct(_Alloc& _Al, _Ty* _Ptr, _Types&&... _Args) {if constexpr (_Uses_default_construct<_Alloc, _Ty*, _Types...>::value) {(void) _Al; // TRANSITION, DevCom-1004719
#ifdef __cpp_lib_constexpr_dynamic_alloc_STD construct_at(_Ptr, _STD forward<_Types>(_Args)...);
#else // __cpp_lib_constexpr_dynamic_alloc::new (static_cast<void*>(_Ptr)) _Ty(_STD forward<_Types>(_Args)...);
#endif // __cpp_lib_constexpr_dynamic_alloc} else {_Al.construct(_Ptr, _STD forward<_Types>(_Args)...);}}template <class _Ty>static _CONSTEXPR20_DYNALLOC void destroy(_Alloc& _Al, _Ty* _Ptr) {if constexpr (_Uses_default_destroy<_Alloc, _Ty*>::value) {
#ifdef __cpp_lib_constexpr_dynamic_alloc_STD destroy_at(_Ptr);
#else // __cpp_lib_constexpr_dynamic_alloc_Ptr->~_Ty();
#endif // __cpp_lib_constexpr_dynamic_alloc} else {_Al.destroy(_Ptr);}}
};

本文依托 MSVC 的源码,讲述了 allocator、list 实现原理以及 allocator_traits 的基本作用,还解答了 rebind 的作用。可以看出这个过程采用了“策略”模式,达到了数据结构与内存管理相解耦的目的。

http://www.dtcms.com/a/511658.html

相关文章:

  • 开题报告--中美外贸企业电子商务模式的比较分析
  • 基于原子操作的 C++ 高并发跳表实现
  • java 8 lambda表达式对list进行分组
  • 网站建设 有聊天工具的吗网站开发者的设计构想
  • 建网站 北京网站接入支付宝在线交易怎么做
  • scrapy爬取豆瓣电影
  • bisheng 的 MCP服务器添加 或 系统集成
  • 一个完整的 TCP 服务器监听示例(C#)
  • 执行操作后元素的最高频率1 2(LeetCode 3346 3347)
  • Java 大视界 -- Java 大数据在智慧交通停车场智能管理与车位预测中的应用实践
  • 版本设计网站100个关键词
  • 网站前置审批工程建设服务平台
  • 共聚焦显微镜(LSCM)的针孔效应
  • STM32CubeMX
  • 网站实现搜索功能四川建设安全协会网站
  • spark组件-spark core(批处理)-rdd特性-内存计算
  • 算法练习:双指针专题
  • 关于comfyui的triton安装(xformers的需求)
  • 爬虫+Redis:如何实现分布式去重与任务队列?
  • 烘焙食品网站建设需求分析wordpress生成静态地图
  • 区块链——Solidity编程
  • OpenSSH安全升级全指南:从编译安装到中文显示异常完美解决
  • 数据结构的演化:从线性存储到语义关联的未来
  • 爱博精电AcuSys 电力监控系统赋能山东有研艾斯,铸就12英寸大硅片智能配电新标杆
  • 基于AI与云计算的PDF操作工具开发技术探索
  • LeetCode 404:左叶子之和(Sum of Left Leaves)
  • 中小企业网站建设论文高端制作网站技术
  • 电子报 网站开发平面设计培训机构排行
  • 无人系统搭载毫米波雷达的距离测算与策略执行详解
  • Adobe Acrobat软件优化配置,启用字体平滑和默认单页连续滚动