STL-----模拟实现一级空间配置器

2023-11-01

众所周知,STL中有六大组件:

  • 容器
  • 容器适配器
  • 仿函数
  • 迭代器
  • 算法
  • 空间配置器

在之前的学习中前面五种我们都已经接触过了,但是在学习其他组件的过程中,我们会发现一个东西隐藏在了一切组件的背后,它就是空间配置器

以STL的实现角度而言,空间配置器是第一个就需要介绍的组件。因为整个STL的操作对象都存放在容器之内,而容器一定需要配置空间来存放数据的。

在我们平时写程序时,需要开辟空间可以使用malloc或者new,那么为什么还要空间配置器呢?今天先来聊一聊一级空间配置器

我们知道的malloc和new的区别之一是malloc开辟内存失败后会返回0,而new开辟内存失败后会抛异常。而一级空间配置器就是更大的程度来合理运用空间。它的内部设计实际就是为了压榨剩余的内存,达到内存的高效运用。所以一级空间配置器内部其实就是malloc和free的封装,然后尽量的开辟出你想要的内存空间,就算系统内部的剩余内存空间小于你所申请的内存空间,它都会努力尝试开辟出来。

接下来一起看看一级空间配置器主要的源码:(我在这里只截取了函数的声明,具体定义在讲解的时候在附上代码)


#ifndef __THROW_BAD_ALLOC
#    include <stdio.h>
#    include <stdlib.h>
#    define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)
#  else 
#    include <new>
#    define __THROW_BAD_ALLOC throw std::bad_alloc()
#  endif
#endif

//一级空间配置器定义开始,注意没有“template型参数”,而inst完全没有排上用场
template <int __inst>
class __malloc_alloc_template 
{
private: 
  //以下函数用来处理内存不足的情况
  static void* _S_oom_malloc(size_t);
  static void* _S_oom_realloc(void*, size_t);
  static void (* __malloc_alloc_oom_handler)();

public:
  //开辟空间
  static void* allocate(size_t __n);

  //释放空间
  static void deallocate(void* __p, size_t /* __n */);

  //重配置空间
  static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz);

  //仿真C++的set_new_handler()函数
  static void (* __set_malloc_handler(void (*__f)()))();

};

一级空间配置器中重要的函数就是allocate、deallocate、reallocate了

allocate()及其相关函数的实现
#define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)   

static void* allocate(size_t __n)
{
  void* __result = malloc(__n);//内部直接调用malloc()分配内存
  //当申请内存失败时,改调用_S_oom_malloc()
  if (0 == __result) __result = _S_oom_malloc(__n);
  return __result;
}

//内存不足的处理函数,初值为0,等待用户自定义
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif

template <int __inst>
void*
__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
    void (* __my_malloc_handler)();
    void* __result;

    for (;;) { //不断的尝试释放、配置、再释放、再配置...
        __my_malloc_handler = __malloc_alloc_oom_handler;
        //由于将内存不足处理函数初值设定为0,若用户没有自定义函数去处理,那么还是抛出异常
        if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
        //用户自定义了内存不足处理函数则调用,企图释放内存
        (*__my_malloc_handler)();
        //继续调用malloc()尝试配置空间
        __result = malloc(__n);
        if (__result) return(__result);
    }
}

我们看到程序中的for循环好像是一个死循环,其实不然。来分析一下,_malloc_alloc_oom_handler初始值为0,如果用户有自定义内存不足处理函数时,那么下面的_my_malloc_handler就不会为0。反之,如果没有自定义的函数,那么就会直接抛出异常. 所以这个循环可以在两个条件下退出:

  • 在反复使用用户定义了的释放内存函数后,我们成功的分配了指定大小的内存,返回指向该内存区域的首地址
  • 用户没有定义相应的处理内存不足函数,直接抛出异常

但是设想,如果用户定义的方法已经不能开辟出空间了,那么这个程序还是一个死循环。所以最好在用户自己定义的处理函数中解决这种情况,可以判断一下有没有释放出空间,有的话就继续,没有的话就可以抛出异常。

reallocate函数

和allocate函数原理一样,这里不多做说明:

#define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)   

static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz)
  {
    void* __result = realloc(__p, __new_sz);//内部直接调用realloc()函数
    //申请内存失败,改调用_S_oom_realloc()函数
    if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
    return __result;
  }

//内存不足的处理函数,初值为0,等待用户自定义
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif

template <int __inst>
void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p, size_t __n)
{
    void (* __my_malloc_handler)();
    void* __result;

    for (;;) {
        __my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*__my_malloc_handler)();
        __result = realloc(__p, __n);
        if (__result) return(__result);
    }
}
释放内存的函数deallocate()
static void deallocate(void* __p, size_t /* __n */)
{
    free(__p); 
}

一级空间配置器是以malloc(),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并实现了C++的set_new_handler()函数,因为它不能直接运用C++的set_new_handler机制。

//static void (* __set_malloc_handler(void (*__f)()))()
{
    //保存旧的内存不足处理函数
    void (* __old)() = __malloc_alloc_oom_handler;
    //重新定义处理函数
    __malloc_alloc_oom_handler = __f;
    return(__old);
}

这个函数的名字和参数列表看起来比较复杂,来看看它的真面目:
这里写图片描述

一级空间配置器的主要内容就这么多了,上一张图再说明一下分配内存的过程:
这里写图片描述

了解了这么多,我们来自己模拟实现一下一级空间配置器,直接上代码:

模拟实现一级空间配置器

malloc_alloc.h

#include <iostream>
#include <stdlib.h>
using namespace std;
#include <Windows.h>

#define _THROW_BAN_ALLOC 0

//一级空间配置器
template<int inst>
class _MallocAllocTemplate
{
public:
    static void* Allocate(size_t n)//开辟空间
    {
        void* result = malloc(n);//调用malloc开辟空间
        if (result == 0)
        {
            result = _Oom_Malloc(n);//开辟失败就调用_Oom_Malloc
        }
        return result;
    }

    static void* Reallocate(void* p, size_t n)//开辟空间,在源空间的基础上
    {
        void* result = realloc(p, n);//调用realloc开辟空间
        if (result == 0)
        {
            result = _Oom_Realloc(p, n);//开辟失败调用_Oom_Realloc
        }
        return result;
    }

    static void Deallocate(void* p)//释放空间
    {
        free(p);//内部调用free释放空间
    }

    //该函数接收一个返回值为空,参数为空的函数指针作为参数,最后返回一个返回值和参数均为空的函数指针
    static void(*Set_Malloc_Handler(void(*f)()))()
    {
        void(*old)() = _Malloc_Alloc_Oom_Handler;//保存旧的处理例程
        _Malloc_Alloc_Oom_Handler = f;          //重新设置新的处理例程
        return old;
    }

private:

    static void(* _Malloc_Alloc_Oom_Handler)();

    static void* _Oom_Malloc(size_t n)
    {
        void(*_My_Malloc_Handler)();
        void* result;

        for (;;)
        {
            _My_Malloc_Handler = _Malloc_Alloc_Oom_Handler;//用户自定义处理函数
            //因为内存不足处理函数初值设置的是0,如果用户没有自定义,就直接抛出异常
            if (_My_Malloc_Handler == 0)
            {
                _THROW_BAN_ALLOC;
            }
            (*_My_Malloc_Handler)();//若定义了,则调用用户自定义处理函数
            result = malloc(n);//再次开辟内存
            if (result)
                return result;
        }
        //不断的尝试释放和配置是因为用户不知道还需要释放多少内存来满足分配需求,只能逐步的释放配置
    }

    static void* _Oom_Realloc(void* p, size_t n)
    {
        void(*_My_Malloc_Handler)();
        void* result;

        for (;;)
        {
            _My_Malloc_Handler = _Malloc_Alloc_Oom_Handler;
            if (_My_Malloc_Handler == 0)
            {
                _THROW_BAN_ALLOC;
            }
            (*_My_Malloc_Handler)();
            result = realloc(p, n);
            if (result)
                return result;
        }
    }
};

//内存不足处理函数初值设置为0,等用户自己定义  
template<int inst>
void(*_MallocAllocTemplate<inst>::_Malloc_Alloc_Oom_Handler)() = 0;

测试代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include "MallocAlloc.h"

//直接将参数inst指定为0
typedef _MallocAllocTemplate<0> Malloc_Alloc;

//自定义内存不足处理函数
void DoFreeAlloc()
{
    for (;;)
    {
        cout << "tyring to release space" << endl;
    }
}

void test()
{
    //测试函数Allocate()
    int* p = (int*)Malloc_Alloc::Allocate(sizeof(int)* 5);
    for (size_t i = 0; i < 5; ++i)
    {
        p[i] = i;
    }
    //测试函数Reallocate()
    p = (int*)Malloc_Alloc::Reallocate(p, sizeof(int)* 10);
    for (int i = 5; i < 10; ++i)
    {
        p[i] = i;
    }
    for (int i = 0; i < 10; ++i)
    {
        cout << p[i] << " ";
    }
    cout << endl;
    Malloc_Alloc::Deallocate(p);

    //自定义一个内存不足处理函数进行测试
    Malloc_Alloc::Set_Malloc_Handler(DoFreeAlloc);
    //开辟一块肯定会申请失败的空间
    int* q = (int*)Malloc_Alloc::Allocate(sizeof(int*)* (102410241024));
    Malloc_Alloc::Deallocate(q);

}

int main()
{
    test();
    system("pause");
    return 0;
}

这里写图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STL-----模拟实现一级空间配置器 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 如何使 Windows 窗体的关闭按钮不关闭窗体但使其不可见?

    该表单有一个 NotifyIcon 对象 当用户单击 关闭 按钮时 我希望表单不关闭而是变得不可见 然后 如果用户想再次查看该表单 可以双击系统托盘中的图标 如果用户想关闭表单 可以右键单击该图标并选择 关闭 有人可以告诉我如何使关闭按钮不
  • Qt-Qlist 检查包含自定义类

    有没有办法覆盖加载自定义类的 Qt QList 的比较机制 即在 java 中你只需要重写一个比较方法 我有一个带有我的自定义类模型的 QList QList
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • C++ 子字符串返回错误结果

    我有这个字符串 std string date 20121020 我正在做 std cout lt lt Date lt lt date lt lt n std cout lt lt Year lt lt date substr 0 4 l
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • 如何将图像和 POST 数据上传到 Azure 移动服务 ApiController 终结点?

    我正在尝试上传图片and POST表单数据 尽管理想情况下我希望它是json 到我的端点Azure 移动服务应用 我有ApiController method HttpPost Route api upload databaseId sea
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • Validation.ErrorTemplate 的 Wpf 动态资源查找

    在我的 App xaml 中 我定义了一个资源Validation ErrorTemplate 这取决于动态BorderBrush资源 我打算定义独特的BorderBrush在我拥有的每个窗口以及窗口内的不同块内
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框

随机推荐

  • ftp服务器一直显示登录密码,ftp一直弹出用户名密码

    大家好 我是时间财富网智能客服时间君 上述问题将由我为大家进行解答 ftp一直弹出用户名密码的原因 1 用户名密码错误 2 有防火墙拒绝连接你的ip 3 服务器架设问题 4 输入的指令错误 5 电脑端口使用的浏览器拒绝连接 建议先尝试匿名登
  • SPI&CAN通信协议简介

    目录 1 SPI SPI的四种工作模式 CPOL clock polarity 时钟极性 CPHA clock phase 时钟相位 四种工作模式 2 CAN CAN的特点 物理层特征 通讯节点 CAN通信报文 数据帧介绍 远程帧 错误帧
  • Swagger与Knife4j的学习

    Swagger与Knife4j的学习 此教程基于黑马程序员Java品达通用权限项目 哔哩哔哩链接 https www bilibili com video BV1tw411f79E p 30 1 swagger介绍 相信无论是前端还是后端开
  • 【华为OD机试真题 JAVA】连续出牌数量

    JS版 华为OD机试真题 JS 连续出牌数量 标题 连续出牌数量 时间限制 1秒 内存限制 262144K 语言限制 不限 有这么一款单人卡牌游戏 牌面由颜色和数字组成 颜色为红 黄 蓝 绿中的一种 数字为0 9中的一个 游戏开始时玩家从手
  • 第五章 神经网络

    5 1 神经元模型 神经网络是由具有适应性的简单单元组成的广泛并行互连的网络 它的组织能够模拟生物神经系统对真实世界物体所做出的交互反应 神经网络中最基本的成分是神经元模型 5 2感知机与多层网络 感知机由两层神经元组成 多层前馈神经网络
  • Zotero软件中复制文献进入不同文件夹和删除文献的方法

    两种方式 方式1 选中文献 拖动文献至目标文件夹 检索文献 只会出现一篇 按住ctrl 可以看到在两个文件夹中均可以访问这篇文献 经验证 在pdf中的标记 两种文件夹访问时显示是同步的 如果想从一个文件夹中去除该文件 选中 键盘点击dele
  • 微信小程序rich-text富文本 解决图片超出屏幕宽度

    问题 微信小程序rich text富文本 图片超出屏幕宽度 文本数据 更改前效果 解决办法 最终效果 文本数据 更改前效果 解决办法 使用正则将数据中的img标签定义class或者直接写样式 再js文件中将数据进行修改 data conte
  • 数论整理之欧几里得算法gcd

    辗转相除法 使用到的原理很聪明也很简单 假设用f x y 表示x y的最大公约数 取k x y b x y 则x ky b 如果一个数能够同时整除x和y 则必能同时整除b和y 而能够同时整除b和y的数也必能同时整除x和y 即x和y的公约数与
  • 软件需求说明书 概要设计说明书 项目开发计划 详细设计说明书 模版

    一 软件需求说明书 1引言2 1 1编写目的2 1 2背景2 1 3定义2 1 4参考资料2 2任务概述2 2 1目标2 2 2用户的特点3 2 3假定和约束3 3需求规定3 3 1对功能的规定3 3 2对性能的规定3 3 2 1精度3 3
  • 对 React 状态管理的理解及方案对比

    1 React 状态 通信 React 为什么需要状态管理 React 特点 专注 view 层 专注 view 层 的特点决定了它不是一个全能框架 相比 angular 这种全能框架 React 功能较简单 单一 UI render da
  • MongoDB学习笔记番外篇:JDBC方式操作MongoDB示例

    1 pom xml中依赖包设置 在项目中引入MongoDB JDBC 驱动mongo java driver X X X jar
  • 2.1-梯度下降

    文章目录 一 调整学习率 1 1 自适应学习率 Adaptive Learning Rates 1 2 Adagrad 二 随机梯度下降 Stochastic Gradient Descent 三 特征缩放 Feature Scaling
  • js如何获取上个月第一天和最后一天

    js如何获取上个月第一天和最后一天 var nowdays new Date var year nowdays getFullYear var month nowdays getMonth if month 0 month 12 year
  • 刷脸支付免费领取保险全额赔付保安全

    现在账户升级后 每位用户都可以免费领取保险 此前未投保的可获得100万保额 已付费投保的则可提额至500万 上支付宝搜 账户安全险 完成升级 即可领到保单 如果账户被盗 不管是手机丢失 还是密码泄露 都能全额赔付 原本由用户支付的保费将由支
  • 解析域名的IP地址

    linux和windows环境下 nslookup windows的powershell环境下 resolve DnsName
  • 怎么用excel管理固定资产

    在当今的数字时代 我们已经习惯了使用各种电子工具来提高我们的生产力 其中 Excel无疑是一个强大的工具 它不仅可以帮助我们处理数据 还可以用来进行复杂的计算和分析 然而 你可能不知道的是 Excel也可以用来管理固定资产 这是一种非常有效
  • 【大唐杯备考】——5G系统勘察设计(学习笔记)

    前言 本期介绍5G系统勘察设计 目录 1 概述 1 1 5G网络预规划 1 1 1 5G建网需求确认 1 1 2 4G现网评估 1 1 3 站点规模估算 1 1 4 5G仿真评估 1 2 5G工程执行 1 2 1 站点勘察 1 2 2 5G
  • 无刷电机换相-六步换向-两两导通与三三导通

    六步换向两两导通和三三导通的区别2 一 三三导通的不常用 之前检索六步换向的方式都为无刷电机如何换向 也未有文献提到三三导通方式 上午检索三三导通方式 文献也极少 其应用多为BLDCM高频链驱动器 查看原因 三三导通可以提高绕组利用率 但容
  • 自动驾驶之入门目录【转】

    从零开始做自动驾驶定位 一 开篇 知乎 从零开始做自动驾驶定位 二 数据集 知乎 从零开始做自动驾驶定位 三 软件框架 知乎 从零开始做自动驾驶定位 四 前端里程计之初试 知乎 从零开始做自动驾驶定位 五 前端里程计之代码优化 知乎 从零开
  • STL-----模拟实现一级空间配置器

    众所周知 STL中有六大组件 容器 容器适配器 仿函数 迭代器 算法 空间配置器 在之前的学习中前面五种我们都已经接触过了 但是在学习其他组件的过程中 我们会发现一个东西隐藏在了一切组件的背后 它就是空间配置器 以STL的实现角度而言 空间