C++内存池(1)理论基础及简单实现

news2024/9/22 17:37:43

一、内存池原理

1、我们先用生活中的例子来解释什么是内存池:
    (1)每个月月底钱花完时,或者急需要用钱时,你就打电话给你父母要钱,然后父母把钱通过微信或支付宝转给你。这种方式,每次要用钱时就需要联系你父母,然后你父母再把钱给你,4年大学下来,这里面耗费的时间也精力肯定不少。
    (2)父母一次性给你一大笔钱,你随便用随便花。这样肯定方便多了,这就类似内存池方式。
    所以内存池的思想,就是在真正使用内存之前,预先申请分配一定数量、大小预设的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存,当内存释放后就回归到内存池留作后续的复用。使得内存使用效率得到提升,一般也不会产生不可控制的内存碎片。
2、那么在C++中怎么实现具体的线程池呢?简单地说,需要下面这几个步骤:
    (1)先申请一块连续的内存空间。
    (2)内存节点:含1个数据对象和指向下一个数据对象的指针。每个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间。
    (3)某个内存节点一旦分配出去,从空闲内存节点链表中移除。
    (4)某个内存节点一旦被释放,将该内存节点重新加入空闲内存节点链表。
    (5)如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。

二、用C++实现内存池

1、定义一个内存节点

template<typename T>
struct Node{
    T data;         // 元素
    Node<T>* next;  // 指向下一个节点的指针
};

2、初始化1个内存块

//当前没有空闲节点
if(this->pMemoryHead == nullptr){
    this->pMemoryHead = new Node<T>; //生成新节点
        
    Node<T> *pTemp = this->pMemoryHead; //将数据连接起来
    for(int i = 1; i < blockSize; i++){
         pTemp->next = new Node<T>;
         pTemp = pTemp->next;
    }
    pTemp->next = nullptr;
        
    this->headCount += blockSize; //可用内存节点数
}

3、申请1个对象空间

Node<T> *pTemp = this->pMemoryHead;
pMemoryHead = pMemoryHead->next; //指向下一个未使用的节点

//因为temp指向的是当前将被使用的节点, 把它的next指向前面的被使用节点
pTemp->next = pUsedHead;
pUsedHead = pTemp; //指针指向最新将被使用的节点
this->headCount--;
this->usedCount++;

4、释放对象空间

Node<T> *pFree = (_Node<T>*)p; //释放的节点
Node<T> *pTemp = pUsedHead;

//第一种情况    
if(pTemp == pFree){  //第一个就是

      pUsedHead = pUsedHead->next;
      pTemp->next = pMemoryHead; //将节点重新接到MemoryHead
      pMemoryHead = pTemp;
      
      this->usedCount--;
      this->headCount++;
      
      return;
}

//第二种情况
Node<T> *prev;
while(pTemp != nullptr){        
    if(pTemp == pFree){    
        prev->next = pTemp->next;
        pTemp->next = MemoryHead;
        MemoryHead = pTemp;
            
        this->usedCount--;
        this->headCount++;

        return;
    }
    //前一个节点
    prev = pTemp;
        

    //查询下一个节点
    pTemp = pTemp->next;
}

三、完整代码

1、MemoryPool.h

#include <iostream>
#include <list>
#include <string>

using namespace std;

template<typename T>
struct Node{        //节点
    T data;         //元素
    Node<T>* next;  //指向下一个节点的指针
};

template <typename T, int blockSize = 10>
class MemoryPool{
public:
    MemoryPool();
    ~MemoryPool();
    
    void* allocate();
    int free(void *p);

    int headCount;
    int usedCount;

private:  
    Node<T>* pMemoryHead;  //未使用的节点
    Node<T>* pUsedHead;    //正在使用的节点
};

2、MemoryPool.cpp

#include"MemoryPool.h"
#include<vector>

template <class T, int blockSize>
MemoryPool<T, blockSize>::MemoryPool(){
     this->pMemoryHead = nullptr;
     this->pUsedHead = nullptr;

     this->headCount = 0;
     this->usedCount = 0;
}

template <class T, int BlockSize>
MemoryPool<T, blockSize>::~MemoryPool(){
  Node<T>* ptr;
	while(this->pMemoryHead){
		ptr = this->pMemoryHead->next;
		delete this->pMemoryHead;
		pMemoryHead = ptr;
	}

	while (this->pUsedHead){
		ptr = this->pUsedHead->next;
		delete this->pUsedHead;
		pUsedHead = ptr;
	}
}

template <class T, int blockSize>
void* MemoryPool<T, blockSize>::allocate(){
    if(this->pMemoryHead == nullptr){
        //当没有空闲节点
        this->pMemoryHead = new Node<T>;

        Node<T> *pTemp = this->pMemoryHead;   //将数据连接起来
        for(int i = 1; i < blockSize; i++){
             pTemp->next = new Node<T>;
             pTemp = pTemp->next;
        }
        pTemp->next = nullptr;
        
        this->headCount += blockSize;
    }

    Node<T> *pTemp = pMemoryHead;
    pMemoryHead = pMemoryHead->next;  //指向下一个数据节点

    //将data 挂在到UseHead
    pTemp->next = pUsedHead;
    pUsedHead = pTemp;
    this->headCount--;
    this->usedCount++;

    return reinterpret_cast<void*>(pTemp);
}

template <typename T, int blockSize>
int MemoryPool<T, blockSize>::free(void *p){
     //遍历 UsedHead链表
    Node<T> *pFind = (Node<T>*)p;
    Node<T> *pTemp = pUsedHead;

    if(pTemp == pFind){  //第一个就是
      pUsedHead = pUseHead->next;
      pTemp->next = pMemoryHead;   //将节点重新接到MemoryHead
      pMemoryHead = pTemp;
      
      this->usedCount--;
      this->headCount++;

      return 1;
    }
    
    Node<T> *prev;
    while(pTemp != nullptr){
        if(pTemp == pFind){
            prev->next = pTemp->next;
            pTemp->next = pMemoryHead;
            pMemoryHead = pTemp;

            this->usedCount--;
            this->headCount++;

            return 1;
        }
        
        prev = pTemp;
        // 查下一个节点
        pTemp = pTemp->next;
    }

    return -1;
}

int main(){
    MemoryPool<int, 100> pa;
    vector<int*> ret;
    cout<<"head:"<<pa.headCount<<"use:"<<pa.usedCount<<endl;
    for(int i=0;i<50;i++){
        ret.push_back(reinterpret_cast<int*>(pa.allocate()));
    }
    cout<<"head:"<<pa.headCount<<"use:"<<pa.usedCount<<endl;

    for(int i=0;i<10;i++){
        pa.free(reinterpret_cast<void*>(ret[i]));
    }
    cout<<"head:"<<pa.headCount<<"use:"<<pa.usedCount<<endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1013402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在谷某地球飞行模拟中导入简单飞机开发的飞机模型

如何在谷某地球飞行模拟中导入简单飞机开发的飞机模型 简飞的飞友们&#xff01;我并没有弃坑&#xff0c;只不过我不是你们想象的那样设计飞机。我之前写过一篇图文讲解如何在谷某地球里规划飞行航线&#xff1a; 手把手教你驾驶西锐SR-22小飞机在美国大峡谷中穿行https://b…

Docker 恶意挖矿镜像应急实例

01、概述 当网络流量监控发现某台运行多个docker容器的主机主动连接到一个疑似挖矿矿池的地址时&#xff0c;需要快速响应和排查&#xff0c;以阻止进一步的损害。 面对docker容器的场景下&#xff0c;如何快速分析和识别恶意挖矿容器?本文将分享一种应急响应思路&#xff0c;…

【基础篇】三、SpringBoot基础配置

文章目录 0、模块的复制1、配置文件格式2、yaml语法3、yaml数据读取方式4、关于封装自定义对象来读取yaml配置的思考4、多环境启动5、配置文件分类 0、模块的复制 平时要大量创建模块时&#xff0c;可以直接复制模块&#xff0c;打开project的目录&#xff1a; 复制粘贴&#…

LeetCode142.环形链表-II

这道题和上一道题几乎没有任何区别啊&#xff0c;为什么还是中等难度&#xff0c;我用上一道题的解法一分钟就写出来了&#xff0c;只不过返回的不是true和false而是节点&#xff0c;以下是我的代码&#xff1a; public class Solution {public ListNode detectCycle(ListNode…

接口自动化测试推荐用什么框架?

在推荐接口自动化测试框架时&#xff0c;需要考虑多个因素&#xff0c;包括项目需求、技术栈、团队经验和个人偏好。 以下是几个常用的接口自动化测试框架供你参考&#xff1a; Postman&#xff1a; Postman是一个功能强大且易于上手的接口测试工具&#xff0c;它提供了许多…

卡尔曼滤波应用在数据处理方面的应用

卡尔曼滤波应用到交通领域 滤波器介绍核心思想核心公式一维卡尔曼滤波器示例导入所需的库 滤波器介绍 卡尔曼滤波器是一种用于估计系统状态的数学方法&#xff0c;它以卡尔曼核心思想为基础&#xff0c;广泛应用于估计动态系统的状态和滤除测量中的噪声。以下是卡尔曼滤波器的核…

学Python的漫画漫步进阶 -- 第十步

学Python的漫画漫步进阶 -- 第十步 十、异常处理10.1 第一个异常——除零异常10.2 捕获异常10.2.1 try-except语句10.2.2 多个except代码块10.2.3 多重异常捕获10.2.4 try-except语句嵌套 10.3 使用finally代码块释放资源10.4 自定义异常类10.5 动动手——手动引发异常10.6 练一…

安卓可视大屏寻呼台 兼容标准sip协议

SV-A32i 安卓可视大屏寻呼台 兼容标准sip协议 A32i 是专门针对行业用户需求研发的一款可视大屏寻呼台产品&#xff0c;配备鹅颈麦克风&#xff0c;支持高清免提通话。基于 Android 9.0 系统&#xff0c;可支持第三方Android 应用安装使用&#xff0c;界面使用便捷。采用 10.1 英…

大模型与数据库:AI 时代的双向助推力

随着 AIGC 的时代到来&#xff0c;以 GPT 为首的大型语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;已经成为当今人工智能领域最热门的话题之一。这些强大的模型不仅在内容创意生成、语言翻译和代码辅助等任务中表现出色&#xff0c;还对数据库的发展带…

DatenLord前沿技术分享 NO.35

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;通过软硬件深度融合的方式打通云云壁垒&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。在本周…

通过机器视觉对硬盘容器上盖的字符进行视觉识别,判断是否混料

 客户的需求  检测内容 硬盘容器上盖字符识别&#xff0c;以判断是否有混料。  检测要求 利用硬盘容器上盖表面字符&#xff0c;来判断是否有混料的情况发生&#xff0c;先识别全部字符。  视觉可行性分析 对贵司的样品进行了光学实验&#xff0c;并进行…

【操作系统】进程,线程和协程的哪些事儿

进程&#xff0c;线程和协程的哪些事儿 进程什么是进程?进程的状态进程的控制结构 线程为什么使用线程&#xff1f;什么是线程&#xff1f;线程与进程的比较线程的实现用户级线程内核级线程轻量级进程 协程协程是什么&#xff1f;协程的优势 区别进程与线程的区别协程与线程的…

【Spatial-Temporal Action Localization(三)】论文阅读2018年

文章目录 1. AVA: A Video Dataset of Spatio-temporally Localized Atomic Visual Actions 时空局部原子视觉动作的视频数据集摘要和结论模型框架思考不足之处时间信息对于识别 AVA 类别有多重要&#xff1f;定位与识别相比有何挑战性&#xff1f;哪些类别具有挑战性&#xff…

Python console cmd命令乱码(无论是os还是subprocess)

给我整无语了&#xff0c;花了一个多小时&#xff0c;根本没找到需要的答案。 网上全是改这样的 五花八门都有&#xff0c;我全部尝试并且还就再排列组合修改&#xff0c;累的。 在下文找到答案&#xff0c;直接os.system(chcp 65001)&#xff0c;问题解决&#xff01;引用文献…

IEC 61850扫盲

目录 1 简介 2 主要特点 2.1 信息分层 2.2 信息模型与通信协议独立 2.3 数据自描述 2.4 面向对象数据统一建模 2.5 带确认服务 2.6 不带确认的服务 2.7 VMD&#xff08;虚拟制造设备&#xff09; 2.8 GOOSE&#xff08;Generic Object Oriented Substation Event&…

LeetCode(力扣)435. 无重叠区间Python

LeetCode435. 无重叠区间 题目链接代码 题目链接 https://leetcode.cn/problems/non-overlapping-intervals/ 代码 class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:if not intervals:return 0intervals.sort(keylambda x: x[0])co…

ajax day4

1、promise链式调用 /*** 目标&#xff1a;把回调函数嵌套代码&#xff0c;改成Promise链式调用结构* 需求&#xff1a;获取默认第一个省&#xff0c;第一个市&#xff0c;第一个地区并展示在下拉菜单中*/let pname axios({url: http://hmajax.itheima.net/api/province,}).t…

徐亦达机器学习:Kalman Filter 卡尔曼滤波笔记 (一)

P ( x t P(x_t P(xt​| x t − 1 ) x_{t-1}) xt−1​) P ( y t P(y_t P(yt​| x t ) x_t) xt​) P ( x 1 ) P(x_1) P(x1​)Discrete State DM A X t − 1 , X t A_{X_{t-1},X_t} AXt−1​,Xt​​Any π \pi πLinear Gassian Kalman DM N ( A X t − 1 B , Q ) N(AX_{t-1}B,Q)…

open-interpreter +GTX1080+wxbot+codellama

https://github.com/KillianLucas/open-interpreter/ Open Interpreter&#xff08;开放解释器&#xff09; 可以让大语言模型&#xff08;LLMs&#xff09;在本地运行代码&#xff08;比如 Python、JavaScript、Shell 等&#xff09;。安装后&#xff0c;在终端上运行 $ inte…

窄边极简折叠玻璃门,实现自由推拉及折叠,遥控随意切换透明与磨砂效果

窄边极简折叠玻璃门是一种非常实用和创新的门设计&#xff0c;可以提供各种功能和效果。以下是一些关于窄边极简折叠玻璃门的相关技巧和应用&#xff1a; 1. 自由推拉和折叠&#xff1a;这种门设计允许你自由地推拉和折叠门&#xff0c;根据需要调整门的宽度和开启面积。这使得…