C++ allocator设计内存管理器

article/2025/10/1 7:27:36

文章目录

  • allocator内存管理器
    • 基本属性
    • 类的设计
    • 关键功能的实现
    • 完整的内存管理器
  • 内存管理器的测试:设计自定义的String类。

前情回顾:
allocator内存管理类

allocator内存管理器

某些类需要在运行时分配可变大小的内存空间,一般来说我们使用容器如vector来管理我们的数据,但是对于某些类,在有些时候我们需要自己进行内存的分配。这些类必须定义自己的拷贝控制成员来管理所分配的内存。

我们将实现一个vector,这个vector保存的是sring类型的数据,而且使用我们自己创建的内存分配的方式。我们的这个类叫做 vecStr


基本属性

我们在自己创建的vecStr中需要有三个属性,分别是:

  • element: 指向分配的内存的起始位置。
  • first_free:指向已经构造完成的对象的下一个位置,即未构造的内存的起始位置。
  • cap:总的内存空间的尾后位置。

如图所示:
在这里插入图片描述

类的设计


基本构造函数:

strVec();
strVec(std::initializer_list<std::string> initList);
~strVec();
strVec(const strVec& other);			//拷贝构造函数
strVec& operator=(const strVec& other);	//拷贝赋值运算符

以下几个具有关键功能的函数:

  • alloc_n_copy:分配内存,并且拷贝给定范围的元素到一个新的内存中。
std::pair<std::string*, std::string*> alloc_n_copy(const std::string* beg, const std::string* end);
  • reallocate: 当内存不够时,重新分配一块新的内存,并且拷贝原始内容,释放旧的内存
void reallocate();
  • free:释放内存空间
//释放内存
void free();
  • check_n_alloc:检查当前内存空间是否足够,不够的话调用reallocate重新分配一块内存。
//检查内存是否足够,不够的话就重新分配
void check_n_alloc();

其他功能性函数:

//获取总容量
size_t capacity()const { return cap - element; }
//获取已分配的容量大小
size_t size()const { return first_free - element; }
void push_back(const std::string& str);

关键功能的实现


  • alloc_n_copy:接受一个开始位置和结束的位置的指针,开辟一块新的内存空间,并且把开始位置到结束位置中的内容拷贝到这块新的内存空间,并且返回这块内存空间的初始位置和尾后位置。

我们使用pair来保存这两个位置。

std::pair<std::string*, std::string*> strVec::alloc_n_copy(const std::string* beg, const std::string* end)
{auto p = allocStr.allocate(end - beg);		//分配end - beg个大小的内存空间,返回未构造的初始的位置//拷贝内存到新的内存空间,uninitialized_copy返回拷贝结束后的位置,这个位置就是first_free的位置,p就是element的位置return { p,std::uninitialized_copy(beg, end, p) };
}

使用uninitialized_copy来拷贝beg到end的内存空间的内容到新的内存空间p中。


  • reallocate:旧内存空间不够时,我们重新开辟一块内存,并且把原始内容拷贝到新内存中,注意我们的新内存一般是原始内存的两倍大小。
  1. 使用move的进行移动构造,从而避免拷贝构造(拷贝后销毁)的繁琐操作,直接进行指针所有权的转移即可,这就是移动构造函数。
  2. 使用construct构造对象。
  3. 注意属性的更新,element first_free cap此时都指向了新的内存空间的对应位置。
void strVec::reallocate()
{/*在重新分配空间的时候,移动而不是拷贝构造*///申请两倍的空间auto NewSpace = (size() == 0) ? 1 : 2 * size();//分配新内存auto pNew = allocStr.allocate(NewSpace);auto dest = pNew;auto old = element;for (size_t i = 0; i != size(); i++){//移动构造旧的内存里的数据,移动到新的内存空间里allocStr.construct(dest++, std::move(*old++));}free();	//释放旧内存//更新数据element = pNew;first_free = dest;cap = element + NewSpace;
}

  • free:释放旧的内存空间,我们使用三种方法来释放旧的内存空间,for_each函数式,正序销毁和逆序销毁,注意,销毁只是调用了他们的析构函数,我们一定最后使用deallocate来彻底释放这块内存。
void strVec::free()
{if (element){
#if 0//for_each销毁std::for_each(begin(), end(), [&](std::string& str){allocStr.destroy(&str);});
#elif 0//逆序销毁旧元素/*for (auto eleBeg = first_free; eleBeg != element;){allocStr.destroy(--eleBeg);}*/
#else//正序销毁旧元素for (auto elebeg = element; elebeg != first_free;){allocStr.destroy(elebeg++);}
#endifallocStr.deallocate(element, cap - element);}
}

完整的内存管理器

#pragma once
#include <iostream>
#include <vector>
#include <memory>
#include <vld.h>
#include <algorithm>/*
内存管理器
*/
class strVec
{
public:strVec();strVec(std::initializer_list<std::string> initList);~strVec();strVec(const strVec& other);			//拷贝构造函数strVec& operator=(const strVec& other);	//拷贝赋值运算符//总容量size_t capacity()const { return cap - element; }//已分配的容量size_t size()const { return first_free - element; }void push_back(const std::string& str);//分配至少能容纳n个元素的内存空间void reserve(const int& n);//重新调整大小void resize(const int& n, const std::string& str = "None");
public:std::string* begin()const { return element; }std::string* end()const { return first_free; }
private://分配内存,并且拷贝元素到这个范围里std::pair<std::string*, std::string*> alloc_n_copy(const std::string* beg, const std::string* end);//释放内存void free();//检查内存是否足够,不够的话就重新分配void check_n_alloc();//内存不够,分配新的内存空间:需要拷贝原来的元素并且释放原来的内存void reallocate();void reallocate(int n);
private:std::allocator<std::string> allocStr;	//内存分配器std::string* element;		//内存空间的起始元素std::string* first_free;	//已分配的实际元素之后的位置std::string* cap;			//总的分配空间之后的位置
};strVec::strVec():element(nullptr), first_free(nullptr), cap(nullptr)
{
}strVec::strVec(std::initializer_list<std::string> initList)
{//分配合适的内存大小int n = initList.size();auto p = allocStr.allocate(n);element = first_free = p;cap = p + n;for (auto& xStr : initList){allocStr.construct(first_free++, xStr);}
}strVec::~strVec()
{free();
}strVec::strVec(const strVec& other)
{//拷贝构造,other的内存拷贝到新的对象中auto pPair = alloc_n_copy(other.element, other.first_free);element = pPair.first;first_free = pPair.second;cap = pPair.second;			//alloc_n_copy分配的空间恰好容纳给定的元素
}strVec& strVec::operator=(const strVec& other)
{//赋值运算符,直接对自身操作,返回自身,记得销毁原来的内存auto pPair = alloc_n_copy(other.element, other.first_free);free();element = pPair.first;first_free = cap = pPair.second;// TODO: 在此处插入 return 语句return *this;
}void strVec::push_back(const std::string& str)
{//插入元素check_n_alloc();	//检查空间大小//构造对象allocStr.construct(first_free++, str);
}void strVec::reserve(const int& n)
{//如果n小于等于当前容量,则什么也不做if (n > capacity()){//重新分配reallocate(n);}
}void strVec::resize(const int& n, const std::string& str)
{if (n < capacity()){//如果说缩小容量,则删除后面的元素int m = capacity() - n;		//容量差值 15-10=5 则删除后五个元素for (int i = 0; i < m; i++){allocStr.destroy(--first_free);}cap = first_free;}else{//否则增大容量,末尾填充strreallocate(n);while (first_free != cap){allocStr.construct(first_free++, str);}}
}std::pair<std::string*, std::string*> strVec::alloc_n_copy(const std::string* beg, const std::string* end)
{auto p = allocStr.allocate(end - beg);		//分配end - beg个大小的内存空间,返回未构造的初始的位置//拷贝内存到新的内存空间,uninitialized_copy返回拷贝结束后的位置,这个位置就是first_free的位置,p就是element的位置return { p,std::uninitialized_copy(beg, end, p) };
}void strVec::free()
{if (element){
#if 0//for_each销毁std::for_each(begin(), end(), [&](std::string& str){allocStr.destroy(&str);});
#elif 0//逆序销毁旧元素/*for (auto eleBeg = first_free; eleBeg != element;){allocStr.destroy(--eleBeg);}*/
#else//正序销毁旧元素for (auto elebeg = element; elebeg != first_free;){allocStr.destroy(elebeg++);}
#endifallocStr.deallocate(element, cap - element);}
}void strVec::check_n_alloc()
{//内存不够if (size() == capacity()){reallocate();}
}void strVec::reallocate()
{/*在重新分配空间的时候,移动而不是拷贝构造*///申请两倍的空间auto NewSpace = (size() == 0) ? 1 : 2 * size();//分配新内存auto pNew = allocStr.allocate(NewSpace);auto dest = pNew;auto old = element;for (size_t i = 0; i != size(); i++){//移动构造旧的内存里的数据,移动到新的内存空间里allocStr.construct(dest++, std::move(*old++));}free();	//释放旧内存//更新数据element = pNew;first_free = dest;cap = element + NewSpace;
}void strVec::reallocate(int n)
{auto NewSpace = n;//分配新内存auto pNew = allocStr.allocate(NewSpace);auto dest = pNew;auto old = element;for (size_t i = 0; i != size(); i++){//移动构造旧的内存里的数据,移动到新的内存空间里allocStr.construct(dest++, std::move(*old++));}free();	//释放旧内存//更新数据element = pNew;first_free = dest;cap = element + NewSpace;
}

内存管理器的测试:设计自定义的String类。

/*
char* 类型的内存分配器
*/class String
{
public:String();String(const char* str);String(const String& other);String& operator=(const String& other);~String();size_t size() const { return end - element; }//重新分配内存
public:private:void free();std::pair<char*, char*> alloc_n_copy(const char* beg,const char* end);//void reallocapacity();char* element;char* end;std::allocator<char> alloCh;	//内存分配器
};int main()
{String s{"woaini"};String s1{ s };String s2;s2 = s1;return 0;
}String::String():element(nullptr),end(nullptr)
{
}String::String(const char* str)
{//分配内存int len = strlen(str);auto pStr =  alloc_n_copy(str, str + len);element = pStr.first;end = pStr.second;
}String::String(const String& other)
{auto pNew = alloc_n_copy(other.element, other.end);element = pNew.first;end = pNew.second;
}String& String::operator=(const String& other)
{auto pNew = alloc_n_copy(other.element, other.end);free();element = pNew.first;end = pNew.second;return *this;
}String::~String()
{free();
}void String::free()
{if (element){std::for_each(element, end, [&](char& str){alloCh.destroy(&str);});alloCh.deallocate(element, end - element);}
}std::pair<char*, char*> String::alloc_n_copy(const char* beg, const char* end)
{auto p =  alloCh.allocate(end - beg);return { p,std::uninitialized_copy(beg,end,p) };
}

http://chatgpt.dhexx.cn/article/vtFhcpL8.shtml

相关文章

Memory Population Guidelines for Intel 3rd Gen Xeon Scalable Processors——内存控制器

Memory Population Guidelines for Intel 3rd Gen Xeon Scalable Processors 英特尔的第三代至强可扩展处理器采用全新的内存控制器架构。本文将深入探讨这些更改&#xff0c;以及如何通过确保正确排序和填充内存模块来最大限度地提高性能。 英特尔的第三代至强可扩展处理器于 …

内存分配器 (Memory Allocator)

对于大多数开发者而言&#xff0c;系统的内存分配就是一个黑盒子&#xff0c;就是几个API的调用。有你就给我&#xff0c;没有我就想别的办法。来UC前&#xff0c;我就是这样认为的。实际深入进去时&#xff0c;才发现这个领域里也是百家争鸣&#xff0c;非常热闹。有操作系统层…

内存分配器

内存分配器&#xff08;Memory Allocator&#xff09;负责内存分配与管理。内存分配器是所有容器的基础&#xff0c;它总是隐藏在容器背后工作&#xff0c;因此内存分配器对实现者非常重要&#xff0c;而对应用者几乎可以忽略。内存分配器分为两级&#xff0c;第一级分配器直接…

小笔记——内存控制器

内存控制器 what is memory controller? 内存控制器是一个用于管理与规划从内存到CPU间传输速度的总线电路控制器。 工作方式 内存控制器控制着必要的逻辑读写DRAM&#xff0c;每隔一段时间刷新动态随机存取存储器&#xff08;DRAM&#xff09;的内容。进行读取和写入动作时&…

DDR内存控制器

DDRDouble Data Rate双倍速率同步动态随机存储器。严格的说DDR应该叫DDR SDRAM&#xff0c;人们习惯称为DDR&#xff0c;其中&#xff0c;SDRAM 是Synchronous Dynamic Random Access Memory的缩写&#xff0c;即同步动态随机存取存储器。而DDR SDRAM是Double Data Rate SDRAM的…

Linux内存控制器(二)

1. memcg_stock_pcp // 每处理器记账缓存一次从内存控制组批量申请32页, 然后把内存控制组的内存使用量加上32页 #define CHARGE_BATCH 32U // 在内存控制组记账(charge)时, 先查看当前处理器的memcg_stock_pcp // 如果memcg_stock_pcp保存的内存控制组(memcg_stock_pcp->c…

DDR控制器

SCL&#xff1a;Self-Calibration Logic&#xff0c;通过寄存器编程方式实现DDR物理层信号校准的逻辑&#xff0c;这部分逻辑全部由硬件实现&#xff0c;软件需要在物理层自动校准之前对寄存器进行初始化。 SDRAM接口宽度在保持相同速率的前提下&#xff0c;可以采用全宽、半宽…

XLINX系列之Zynq-7000系列DDR内存控制器详解

1DDR内存控制器介绍 DDR内存控制器支持DDR2&#xff0c;DDR3&#xff0c;DDR3L和LPDDR2设备&#xff0c;包括三个主要块&#xff1a;AXI存储器端口接口&#xff08;DDRI&#xff09;&#xff0c;带有交易调度器&#xff08;DDRC&#xff09;的核心控制器和具有数字PHY&#xf…

3. 内存控制器与SDRAM

内存控制器&#xff08;内存接口设备&#xff09; 地址处于不同的范围&#xff0c;会发出不同的片选引脚&#xff0c;换句话说&#xff0c;SOC外接的不同内存芯片&#xff0c;会有不同的地址范围。 CPU统一编址包括GPIO&#xff0c;各种协议类接口控制器&#xff08;UART&…

存储控制器

存储控制器是按照一定的时序规则对存储器的访问进行必要控制的设备&#xff0c;包括地址信号、数据信号以及各种命令信号的控制&#xff0c;使主设备(访问存储器的设备)能够根据自己的要求使用存储器上的存储资源。 存储控制器的作用主要就是进行接口的转换&#xff0c;将主设…

内存控制器

1.内存控制器&#xff08;Memory Controller&#xff09; 内存控制器&#xff08;Memory Controller&#xff09;是计算机系统内部控制内存并且通过内存控制器使内存与CPU之间交换数据的重要组成部分。内存控制器决定了计算机系统所能使用的最大内存容量、内存BANK数、内存类型…

Java并发包中常用类小结(一)

Java并发包中常用类小结(一) 从JDK1.5以后&#xff0c;Java为我们引入了一个并发包&#xff0c;用于解决实际开发中经常用到的并发问题&#xff0c;那我们今天就来简单看一下相关的一些常见类的使用情况。 1、ConcurrentHashMap ConcurrentHashMap其实就是线程安全版本的has…

java并发包源码分析

java并发包之AbstractQueuedSynchronizer源码分析 分析并发包首先要了解AbstractQueuedSynchronizer&#xff08;AQS&#xff09;&#xff0c;因为AQS是并发包的基础工具类。本文从ReentrantLock的公平锁出发&#xff0c;分析AbstractQueuedSynchronizer的工作过程。 lock与u…

【Java进阶】Java并发包提供了哪些并发工具类?

通过前面的学习&#xff0c;我们一起回顾了线程、锁等各种并发编程的基本元素&#xff0c;也逐步涉及了 Java 并发包中的部分内容&#xff0c;相信经过前面的热身&#xff0c;我们能够更快地理解 Java 并发包。 今天我要问你的问题是&#xff0c;Java 并发包提供了哪些并发工具…

java---JUC并发包详解

目录 前言 一、atomic包 AtomicInteger类 AtomicReference类 AtomicStampedReference类 二、locks包 接口 Condition Lock ReadWriteLock 实现类 ReentrantLock类 ReentrantReadWriteLock类 三、CountDownLatch 四、Semaphore(信号量) 总结 前言 JUC是java.u…

Java多线程并发编程--Java并发包(JUC)

Java多线程并发–Java并发包&#xff08;JUC&#xff09; 前言 前一篇文章中&#xff0c;笔者已经介绍了Java多线程的一些基础知识&#xff0c;但是想要成为一名中高级Java程序员还必须懂得Java并发包&#xff08;JUC&#xff09;的知识点&#xff0c;而且JUC现在也是面试中必…

接口测试--apipost如何自定义header中的content-type

使用apipost进行接口测试的时候&#xff0c;有时候会用到一些自定义或者不常见的content-type格式&#xff0c;这个时候就要手动在header头部自定义content-type。 这里我们自定义一个content-type&#xff0c;格式为application/octet-stream 然后body选择的为form-data&…