• C++模板与STL(六):内存空间配置器及内存池技术模拟


    目录

    1.容器内存空间配置器的概念 

    2. Vector List deque的内存分配策略

    2.1 Vector的内存分配策略模拟

    2.2 List的内存分配策略

    2.3 deque的内存分配策略

    3.内存池技术及其仿真


    1.容器内存空间配置器的概念 

    Allocator:对容器与内存分布进行管理的承担者

    2. Vector List deque的内存分配策略

    2.1 Vector的内存分配策略模拟

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. //空间配置器的基类模板
    7. template<class _Ty>
    8. struct Allocator_base {
    9. typedef _Ty value_type;
    10. };
    11. template<class _Ty>
    12. struct Allocator_base<const _Ty> {
    13. typedef _Ty value_type;
    14. };
    15. //空间配置器的类模板设计
    16. template<class _Ty>
    17. class Allocator :public Allocator_base<_Ty> {
    18. public:
    19. //内嵌数据类型表
    20. typedef typename std::size_t size_type;
    21. typedef typename std::ptrdiff_t difference_type;
    22. typedef _Ty* pointer;
    23. typedef const _Ty* const_pointer;
    24. typedef _Ty& reference;
    25. typedef const _Ty& const_reference;
    26. typedef Allocator_base<_Ty> _Mybase;
    27. typedef typename _Mybase::value_type value_type;
    28. //配置器类型的嵌入类型
    29. template<class _U>
    30. struct rebind {
    31. typedef Allocator<_U>other;
    32. };
    33. //构造函数
    34. Allocator()throw() {
    35. }
    36. Allocator(const Allocator&)throw() {}
    37. //可以接受不同类型的其他allocator的拷贝构造
    38. template<class _otherAll>
    39. Allocator(const Allocator<_otherAll>&)throw() {}
    40. ~Allocator()throw()
    41. {
    42. }
    43. //请求内存空间
    44. pointer allocate(size_type num, typename Allocator<_Ty>::const_pointer hint = 0) {
    45. //显示一下内存空间发呢配信息
    46. static int i = 0;
    47. i++;
    48. cout << endl;
    49. cout << "第" << i << "次分配" << "-----------------------" << endl;
    50. cout << "本次需要分配可容纳" << num << "个元素空间" << endl;
    51. return (pointer)(::operator new(num * sizeof(_Ty)));
    52. }
    53. //在已经请求的内存空间中构造容器元素对象:operator new之后,用placement new指向
    54. void construct(pointer p, const_reference value) {
    55. new(p)_Ty(value);
    56. }
    57. //析构容器元素对象
    58. void destroy(pointer p) {
    59. p->~_Ty();
    60. }
    61. //释放已有的内存空间
    62. void deallocate(pointer p, size_type size) {
    63. ::operator delete(p);
    64. }
    65. };
    66. //调用
    67. template<typename _T>
    68. void print(vector<_T, Allocator<_T>>& iv) {
    69. cout << "现在空间可容纳" << iv.capacity() << "个元素";
    70. cout << "\t 容器当前所存储元素数目:" << iv.size() << endl;
    71. cout << "容器中的元素数据:";
    72. for_each(iv.begin(), iv.end(), [](int n) {cout << " " << n; });
    73. cout << endl;
    74. }
    75. int main() {
    76. int a[] = { 1,2,3 };
    77. vector<int, Allocator<int>>vec(a, a + 3);
    78. print<int>(vec);
    79. for (int i = 1; i < 10; i++) {
    80. vec.push_back(10 * i);
    81. print<int>(vec);
    82. }
    83. return 0;
    84. }

    可以看到每次分配的内存空间均为上次的1.5倍,这也是VS编译器所采取的策略。

    不同C++编译器分配内存策略不同,例如g++就是2倍。

    2.2 List的内存分配策略

    对之前的代码做以下修改,变为list的,其他代码保持不变:

    1. //调用
    2. template<typename _T>
    3. void print(list<_T, Allocator<_T>>& iv) {
    4. //cout << "现在空间可容纳" << iv.capacity() << "个元素";
    5. cout << "\t 容器当前所存储元素数目:" << iv.size() << endl;
    6. cout << "容器中的元素数据:";
    7. for_each(iv.begin(), iv.end(), [](int n) {cout << " " << n; });
    8. cout << endl;
    9. }
    10. int main() {
    11. int a[] = { 1,2,3 };
    12. //vector>vec(a, a + 3);
    13. list<int, Allocator<int>>myList(a, a + 3);
    14. print<int>(myList);
    15. for (int i = 1; i < 5; i++) {
    16. myList.push_back(10 * i);
    17. print<int>(myList);
    18. }
    19. return 0;
    20. }

     

    可以看到list的分配内存方式是一个一个分配的,需要一个就分配一个,不留余量。 

    2.3 deque的内存分配策略

    1. //调用
    2. template<typename _T>
    3. void print(deque<_T, Allocator<_T>>& iv) {
    4. //cout << "现在空间可容纳" << iv.capacity() << "个元素";
    5. cout << "\t 容器当前所存储元素数目:" << iv.size() << endl;
    6. cout << "容器中的元素数据:";
    7. for_each(iv.begin(), iv.end(), [](int n) {cout << " " << n; });
    8. cout << endl;
    9. }
    10. int main() {
    11. int a[] = { 1,2,3 };
    12. //vector>vec(a, a + 3);
    13. //list>myList(a, a + 3);
    14. deque<int,Allocator<int>>deq(a, a + 3);
    15. print<int>(deq);
    16. for (int i = 1; i < 200; i++) {
    17. deq.push_back(i);
    18. //print(deq);
    19. }
    20. return 0;
    21. }

     deque每次分配固定大小内存,每次4个,逐次累加。

    3.内存池技术及其仿真

    1. #include
    2. #define MEMPOOL_ALIGNMENT 8
    3. using namespace std;
    4. //内存块
    5. template<typename _T>
    6. struct MemoryBlock{
    7. //表域
    8. int nSize;//内存块大小,以字节为单位
    9. int nFree;//表示内存剩余可分配的单位
    10. int nFirst;//首个剩余单位的序号
    11. MemoryBlock* pNext;
    12. char aData[1];//内存空间开始位置标记
    13. MemoryBlock(int nUnitSize, int nUninAmount) :
    14. nSize(nUnitSize* nUninAmount), nFree(nUninAmount - 1), nFirst(1), pNext(nullptr)
    15. {
    16. //下一个可以分配的单位序号需要记录当前单元的强两个字节
    17. char* ppData = aData;
    18. cout << "存储区首地址ppData=" << (int)*ppData << endl;
    19. for (int i = 1; i < nUninAmount; i++) {
    20. //在当前存储单位空间 存储下一个可分配的单位序号
    21. (*(unsigned short*)ppData) = i;
    22. ppData += nUnitSize;
    23. }
    24. cout << "内存块的构造函数被调用了" << endl;
    25. }
    26. //nUnitSize:存储单位大小
    27. //nUnitAmount:内存块的规模(数量)
    28. void* operator new(size_t, int nUnitSize, int nUnitAmount) {
    29. cout << "分配内存空间并创建MemoryBlock" << endl;
    30. return ::operator new(sizeof(MemoryBlock) + nUnitSize * nUnitAmount);
    31. }
    32. void operator delete(void* pBlock) {
    33. cout << "调用了内存的析构" << endl;
    34. }
    35. };
    36. //内存池 Mempool
    37. template<typename _T>
    38. struct MemoryPool
    39. {
    40. int nInitSize;
    41. int nGrowSize;
    42. int nUnitSize;
    43. MemoryBlock<_T>* pBlock;
    44. MemoryPool(int _nGrowSize = 10, int _nInitSize = 3) {
    45. cout << "调用了内存池的构造函数" << endl;
    46. nInitSize = _nInitSize;
    47. nGrowSize = _nGrowSize;
    48. pBlock = nullptr;
    49. nUnitSize = (sizeof(_T));
    50. //纯出单位的大小调整
    51. if (sizeof(_T) > 4) {
    52. //8字节对齐
    53. nUnitSize = (sizeof(_T) + (MEMPOOL_ALIGNMENT - 1)) &
    54. ~(MEMPOOL_ALIGNMENT - 1);
    55. }
    56. else {
    57. if (sizeof(_T) < 2) { nUnitSize = 2; }
    58. else { nUnitSize = 4; }
    59. }
    60. }
    61. ~MemoryPool() {
    62. MemoryBlock<_T>* pMyBlock = pBlock;
    63. while (pMyBlock != nullptr) {
    64. pMyBlock = pMyBlock->pNext;
    65. delete(pMyBlock);
    66. }
    67. cout << "调用了内存池的析构函数" << endl;
    68. }
    69. void* allocate(size_t num) {
    70. for (int i = 0; i < num; i++) {
    71. if (pBlock == nullptr) {
    72. //创建首个内存块
    73. pBlock = (MemoryBlock<_T>*)new(nUnitSize, nInitSize)MemoryBlock<_T>(nUnitSize, nInitSize);
    74. return (void*)pBlock->aData;
    75. }
    76. //为内存寻求合适的内存块
    77. MemoryBlock<_T>* pMyBlock = pBlock;
    78. while (pMyBlock != nullptr && pMyBlock->nFree == 0) {
    79. pMyBlock = pMyBlock->pNext;
    80. }
    81. if (pMyBlock != nullptr) {
    82. cout << "内存空间已经找到,First=" << pMyBlock->nFirst << endl;
    83. char* pFree = pMyBlock->aData + pMyBlock->nFirst * nUnitSize;
    84. pMyBlock->nFirst = *((unsigned short*)pFree);
    85. pMyBlock->nFree--;
    86. return (void*)pFree;
    87. }
    88. else {
    89. //说明内存用完了
    90. if (nGrowSize == 0) {
    91. return nullptr;
    92. }
    93. cout << "分配新内存" << endl;
    94. pMyBlock = (MemoryBlock<_T>*)new (nUnitSize, nGrowSize)MemoryBlock<_T>(nUnitSize, nGrowSize);
    95. if (pMyBlock == nullptr) {
    96. return nullptr;
    97. }
    98. //如果成功那么,将新内存插入链表
    99. pMyBlock->pNext = pBlock;
    100. pBlock = pMyBlock;
    101. return (void*)pMyBlock->aData;
    102. }
    103. }
    104. }
    105. void free(void* pFree) {
    106. cout << "释放存储单位的空间" << endl;
    107. //找到p所在的内存块
    108. MemoryBlock<_T>* pMyBlock = pBlock;
    109. MemoryBlock<_T>* PreBlock = nullptr;
    110. while (pMyBlock != nullptr && (pBlock->aData > pFree)
    111. || pMyBlock->aData + pMyBlock->nSize) {
    112. PreBlock = pMyBlock;
    113. pMyBlock = pMyBlock->pNext;
    114. }
    115. //该内存在本内存池中的指向位置
    116. if (pMyBlock != nullptr) {
    117. //修改数组链表
    118. *((unsigned short*)pFree) = pMyBlock->nFirst;
    119. pMyBlock->nFirst = (unsigned short)((unsigned long)pFree - (unsigned long)pMyBlock->aData) / nUnitSize;
    120. pMyBlock->nFree++;
    121. //判断是否需要向操作系统释放内存
    122. if (pMyBlock->nSize == pMyBlock->nFree * nUnitSize) {
    123. delete(pMyBlock);
    124. }
    125. else {
    126. PreBlock = pMyBlock->pNext;
    127. pMyBlock->pNext = pBlock;
    128. pBlock = pMyBlock;
    129. }
    130. }
    131. }
    132. };
    133. class UserCls {
    134. int s;
    135. double s1;
    136. double s2;
    137. public:
    138. UserCls(int x) :s(x) {
    139. cout << "调用了UserCls的构造函数" << endl;
    140. }
    141. int get() { return s; }
    142. ~UserCls()
    143. {
    144. cout << "调用了UserCls的析构函数" << endl;
    145. }
    146. };
    147. int main() {
    148. MemoryPoolM_Pool;
    149. UserCls* dp1 = (UserCls*)M_Pool.allocate(1);
    150. cout << "dp1=" << dp1 << endl;
    151. new(dp1)UserCls(111);
    152. cout << "dp1中的数据是" << dp1->get() << endl;
    153. cout << endl;
    154. UserCls* dp2 = (UserCls*)M_Pool.allocate(1);
    155. cout << "dp2=" << dp2 << endl;
    156. new(dp2)UserCls(222);
    157. cout << "dp2中的数据是" << dp2->get() << endl;
    158. cout << endl;
    159. UserCls* dp3 = (UserCls*)M_Pool.allocate(1);
    160. cout << "dp3=" << dp3 << endl;
    161. new(dp3)UserCls(333);
    162. cout << "dp3中的数据是" << dp3->get() << endl;
    163. cout << endl;
    164. UserCls* dp4 = (UserCls*)M_Pool.allocate(1);
    165. cout << "dp4=" << dp4 << endl;
    166. new(dp4)UserCls(444);
    167. cout << "dp4中的数据是" << dp4->get() << endl;
    168. cout << endl;
    169. UserCls* dp5 = (UserCls*)M_Pool.allocate(1);
    170. cout << "dp5=" << dp5 << endl;
    171. new(dp5)UserCls(555);
    172. cout << "dp5中的数据是" << dp5->get() << endl;
    173. cout << endl;
    174. return 0;
    175. }

  • 相关阅读:
    Java8实战-总结30
    Spring Cloud中,Eureka常见问题总结
    力扣第617题 合并二叉树 c++ 前中后序 完成 附加迭代版本
    信息学奥赛一本通:1397:简单算术表达式求值
    问题解析:为什么数组下标从0 开始而不是 1 ?
    [C]详解C语言动态内存分配
    Win11快速助手在哪里?Win11打开快速助手的方法
    【Unity3D】资源管理
    C++ 实现HTTP的客户端、服务端demo和HTTP三方库介绍
    【C++--类和对象】构造函数&&析构函数
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126252707