我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++设计一个简单内存池的全过程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++设计一个简单内存池的全过程

什么是内存池???

通常我们用new或malloc来分配内存的话,由于申请的大小不确定,所以当频繁的使用时会造成内存碎片和效率的降低。为了克服这种问题我们提出了内存池的概念。内存池是一种内存分配方式。内存池的优点就是可以有效的减少内存碎片化,分配内存更快速,减少内存泄漏等优点。

内存池是在真正使用内存之前,先申请分配一个大的内存块留作备用。当真正需要使用内存的时候,就从内存池中分配一块内存使用,当使这块用完了之后再还给内存池。若是内存块不够了就向内存再申请一块大的内存块。

可以看出这样做有两个好处:

  1、由于向内存申请的内存块都是比较大的,所以能够降低外碎片问题。

  2、一次性向内存申请一块大的内存慢慢使用,避免了频繁的向内存请求内存操作,提高内存分配的效率。

内存碎片化:

造成堆利用率很低的一个主要原因就是内存碎片化。如果有未使用的存储器,但是这块存储器不能用来满足分配的请求,这时候就会产生内存碎片化问题。内存碎片化分为内部碎片和外部碎片。

内碎片:

内部碎片是指一个已分配的块比有效载荷大时发生的。(举个栗子:假设以前分配了10个大小的字节,现在只用了5个字节,则剩下的5个字节就会内碎片)。内部碎片的大小就是已经分配的块的大小和他们的有效载荷之差的和。因此内部碎片取决于以前请求内存的模式和分配器实现的模式。

外碎片:   外部碎片就是当空闲的存储器的和计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以处理这个请求。外部碎片取决于以前的请求内存的模式和分配器的实现模式,还取决于于将来的内存请求模式。所以外部碎片难以量化。

下面介绍一种简单的内存池,它是针对于某种对象实现的。 我们可以用一个链表实现这个内存池,链表上的每个结点都是一个对象池,如果我们需要申请空间的话,直接去内存池里面申请空间,当用完之后再还给内存池。

内存池的设计主要包含三步:

1、初始化

在创建内存池的时候为内存池分配了一块很大的内存,便于以后的使用。

2、分配内存

当需要内存的时候就去内存池里面分配内存。

3、回收内存

当从内存池里面分配来的内存使用完毕之后,需要将这块内存还给内存池。

设计上面这个内存池最重要的问题就是如何重复利用释放回来的内存,让利用率达到最高???

但是如果当对象的大小小于对象指针的时候,也就是一个对象的空间存不下一个指针的大小,这时候就不可避免的产生内碎片。   例如:为T类型对象开辟对象池,sizeof(T)<sizeof(T*),这时候我们就要为一个T类型对象申请sizeof(T*)大小的内存。


代码实现:
#pragma once
#include<iostream>
using namespace std;
//用链表来实现内存池,每一个结点都挂有一块内存
template<typename T>
class ObjectPool
{
       struct BlockNode         //每一个结点类型
       {
              void* _memory;        //指向一块已经分配的内存
              BlockNode * _next;    //指向下一个结点
              size_t _objNum;       //记录这块内存中对象的个数
              BlockNode(size_t objNum)
                     :_objNum(objNum)
                     , _next(NULL)
              {
                     _memory = malloc(_objNum*_itemSize);
              }
              ~BlockNode()
              {
                     free(_memory);
                     _memory = NULL;
                     _next = NULL;
                     _objNum = 0;
              }
       };
protected:
       size_t _countIn;      //当前结点的在用的计数
       BlockNode* _frist;    //指向链表的头
       BlockNode* _last;     //指向链表的尾
       size_t _maxNum;        //记录内存块最大的容量
       static size_t _itemSize;   //单个对象的大小
       T* _lastDelete;        //指向最新释放的那个对象的空间
public:
       ObjectPool(size_t initNum = 32, size_t maxNum = 100000)  //默认最开始内存块有32个对象,一个内存块最大有maxNum个对象
              :_countIn(0)
              , _maxNum(maxNum)
              , _lastDelete(NULL)
       {
              _frist = _last =new BlockNode(initNum);   //先开辟一个结点,这个结点里面的内存块能够存放initNum个对象
       }
       ~ObjectPool()
       {
              Destory();
       }
       T* New()                   //分配内存
       {
              if (_lastDelete)         //先到释放已经用完并且换回来的内存中去找
              {
                     T* object = _lastDelete;
                     _lastDelete = *((T**)_lastDelete);  //将_lastDelete转换成T**,*引用再取出来T*,也就是取出前T*类型大小的单元
                     return new(object) T();        //把这块内存用从定位new初始化一下
              }
              //判断还有没有已经分配的内存且还未使用,如果没有内存的话就要再分配内存
              if (_countIn >= _last->_objNum)     //大于等于表示没有了,这时候就要分配内存了
              {
                     size_t size =2*_countIn;
                     if (size > _maxNum)            //块的最大大小不能超过maxNum,如果没超过就以二倍增长
                           size = _maxNum;
                     _last->_next = new BlockNode(size);
                     _last = _last->_next;
                     _countIn = 0;
              }
              //还有已经分配好的未被使用的内存
              T* object =(T*)((char*)_last->_memory + _countIn*_itemSize);
              _countIn++;
              return new(object) T();        //将这块空间用重定位new初始化一下
       }
       void Destory()
       {
              BlockNode *cur = _frist;
              while (cur)
              {
                     BlockNode* del = cur;
                     cur = cur->_next;
                     delete del;            //会自动调用~BlockNode()
              }
              _frist = _last = NULL;
       }
       void Delete(T* object)          //释放内存
       {
              if (object)
              {
                     object->~T();       
                     *((T**)object) = _lastDelete;      //将_lastDelete里面保存的地址存到tmp指向空间的前T*大小的空间里面
                     _lastDelete = object;
              }
       }
protected:
       static size_t GetItemSize()
       {
              if (sizeof(T)>sizeof(T*))
              {
                     return sizeof(T);
              }
              else
              {
                     return sizeof(T*);
              }
       }
};
template<typename T>
size_t ObjectPool<T>::_itemSize =ObjectPool<T>::GetItemSize();          //类外初始化静态变量_itemSize

总结

到此这篇关于C++设计一个简单内存池的文章就介绍到这了,更多相关C++设计内存池内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++设计一个简单内存池的全过程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++怎么实现一个简单的线程池

本文小编为大家详细介绍“C++怎么实现一个简单的线程池”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++怎么实现一个简单的线程池”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、设计线程池应该包括保存线程的容
2023-06-30

如何通过C++编写一个简单的计算器程序?

如何通过C++编写一个简单的计算器程序?计算器程序是许多程序员在学习编程过程中的一个常见项目。通过编写一个简单的计算器程序,可以帮助我们学会使用C++语言基础知识,如变量、运算符和控制流语句。以下是一个简单的计算器程序的示例代码。#incl
如何通过C++编写一个简单的计算器程序?
2023-11-02

如何通过C++编写一个简单的计时器程序?

如何通过C++编写一个简单的计时器程序?计时器是人们生活中常见的一个工具,它可以用来计算时间、测量时间间隔或者进行计时操作。对于初学者来说,编写一个简单的计时器程序既可以提高编程技能,也可以增加对C++语言的理解。本文将介绍如何使用C++编
如何通过C++编写一个简单的计时器程序?
2023-11-02

如何通过C++编写一个简单的倒计时程序?

C++ 是一种广泛使用的编程语言,在编写倒计时程序方面非常方便和实用。倒计时程序是一种常见的应用,它能为我们提供非常精确的时间计算和倒计时功能。本文将介绍如何使用 C++ 编写一个简单的倒计时程序。实现倒计时程序的关键就是使用计时器来计算时
如何通过C++编写一个简单的倒计时程序?
2023-11-03

如何通过C++编写一个简单的日历程序?

如何通过C++编写一个简单的日历程序?日历是我们日常生活中不可或缺的工具,它帮助我们记录时间、安排事务和计划活动。在本文中,我将分享如何使用C++编写一个简单的日历程序,让我们一起来看看实现的步骤吧!步骤1:确定程序的基本功能在开始编写程序
如何通过C++编写一个简单的日历程序?
2023-11-04

如何通过C++编写一个简单的地址簿程序?

如何通过C++编写一个简单的地址簿程序?引言:在现代社会中,地址簿是一种常见的工具,用于存储和管理个人联系人的基本信息,如姓名、电话号码和地址等。在本文中,我们将详细介绍如何使用C++编写一个简单的地址簿程序。正文:步骤1:定义地址簿条目的
如何通过C++编写一个简单的地址簿程序?
2023-11-02

如何通过C++编写一个简单的日记本程序?

如何通过C++编写一个简单的日记本程序?日记本是许多人记录生活、思考和感受的工具。通过编写一个简单的日记本程序,可以更加方便和高效地记录并管理个人的日记。在本文中,将介绍如何使用C++语言编写一个简单的日记本程序。首先,我们需要确定日记本程
如何通过C++编写一个简单的日记本程序?
2023-11-03

如何通过C++编写一个简单的记账本程序?

本文将介绍如何使用C++编写一个简单的记账本程序,随着生活成本的不断上升,越来越多的人开始关注自己的财务状况。使用记账本可以记录收支情况,提高理财能力,C++语言的优势在于其高效性和可移植性,非常适合编写此类程序。1.确定程序功能和需求在编
如何通过C++编写一个简单的记账本程序?
2023-11-03

如何通过C++编写一个简单的图片处理程序?

在本文中,我们将了解如何通过C ++编写一个简单的图像处理程序。我们将涵盖从读取图像到应用过滤器和保存图像的所有基础知识。在开始编写图像处理程序之前,您需要安装OpenCV库。 OpenCV是一个流行的计算机视觉库,具有丰富的功能,可帮助您
如何通过C++编写一个简单的图片处理程序?
2023-11-03

如何通过C++编写一个简单的文件加密程序?

如何通过C++编写一个简单的文件加密程序?导语:随着互联网的发展和智能设备的普及,保护个人资料和敏感信息的重要性越来越显著。为了确保文件的安全性,常常需要对其进行加密。本文将介绍如何使用C++编写一个简单的文件加密程序,以保护你的文件免受未
如何通过C++编写一个简单的文件加密程序?
2023-11-03

如何通过C++编写一个简单的天气查询程序?

在这个数字化时代,天气预报已经成为了我们生活中的重要组成部分之一。通过天气预报,我们能够了解到未来天气的变化,从而做好相应的准备。而现今,天气查询程序也已经成为了我们日常生活中的重要工具之一。如果您想学习如何通过编程语言来创建一个简单的天气
如何通过C++编写一个简单的天气查询程序?
2023-11-03

如何通过C++编写一个简单的音频播放器程序?

随着计算机技术的进步,音频播放器已经成为人们的日常生活中必不可少的工具。而在C++编程领域,如何编写一个简单的音频播放器程序呢?在本文中,我们将会探讨这个话题。一、 程序的基本框架编写一个音频播放器程序的时候,最重要的是要明确程序的基本框架
如何通过C++编写一个简单的音频播放器程序?
2023-11-04

如何通过C++编写一个简单的网页编辑器程序?

如何通过C++编写一个简单的网页编辑器程序?概述:随着互联网的普及,网页成为人们获取信息和展示内容的重要手段。为了满足用户的需求,开发一个简单易用的网页编辑器至关重要。本文将介绍如何使用C++编写一个基于控制台的简单网页编辑器程序,帮助读者
如何通过C++编写一个简单的网页编辑器程序?
2023-11-04

如何利用C++实现一个简单的网站访问统计程序?

随着互联网的迅速发展,越来越多的网站开始关注网站访问数据的统计,并将这些数据用于网站的优化和改进。因此,开发一个简单的网站访问统计程序对于网站管理者来说非常有用。而其中一个实现这一目标的可能性是通过使用C++,该语言可以帮助您以更高效的方式
如何利用C++实现一个简单的网站访问统计程序?
2023-11-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录