【了解空间配置器、STL关联式容器总结】

一、空间配置器
1、什么是空间配置器?

空间配置器是为了各个容器高效管理空间(实行空间申请与回收)而设置的内存管理机制

2、为什么需要空间配置器?

在模拟实现很多容器的时候发现很多空间都是通过new动态申请的,故:
a)动态申请的空间需要用户自己进行释放容易造成内存泄漏;
b)频繁申请小块内存容易造成内存碎片,影响程序运行效率;
c)malloc和new申请的空间,每块存在额外空间浪费,且申请空间失败没有应对方法;
d)未考虑线程安全问题

3、STL空间配置器的实现原理

  • 以128作为小块内存与大块内存的分界线,将空间配置器分为两级结构;
    (1)一级空间配置器
    (对大于128的大块空间进行处理)
    直接对malloc和free进行了封装,增加了set_new_handle的思想
    (set_new_handle函数就是设置new_p指向的函数new或new[]失败时的回调函数
    malloc失败回调函数原理:
    a)检测用户是否设置空间不足应对措施,没有则抛异常;
    b)若已设置则执行用户提供空间不足应对措施;
    c)重新进行空间申请,可能就会成功;

(2)二级空间配置器
专门负责处理小于128字节的小块内存,SGI-STL(STL的空间配置器),采用内存池技术来提高速度减少额外空间的浪费,利用哈希桶来提高用户获取空间的速度与高效管理;

  • 内存池

先申请一块比较大的内存块已做备用,当需要内存时,直接到内存池中去去,当池中空间不够时,再向内存中去取,当用户不用时,直接还回内存池即可。避免了频繁向系统申请小块内存所造成的效率低、内存碎片以及额外浪费的问题

在这里插入图片描述问题:

1、内存池分配空间的流程
答:
现在已经归还的内存块中找合适的块
找到---->是否需要分割---->不需要---->直接分配
需要----->再对内存块进行分割
未找到----->去内存池中申请
2、 当用户需要空间时,能否直接从内存池中大块空间中直接截取?为什么?
答:优先从链表中选,先从大块中拿,如果用户需要大块的空间,可能就给不了了
3、 对用户归还的空间能否直接拼接在大块内存前?
答:不行
4、对用户归还的空间如何进行管理?
答:用链表连接起来
5、不断切割会有什么后果?
答:内存碎片

  • 因为在二级空间配置器中使用了内存池技术,并且对于归还空间的管理使用了哈希桶进行管理,那么是如何利用哈希桶对已归还空间进行管理的呢?
    答:
    用户申请的空间基本上都是4的整数倍,其他大小的空间很少用到,所以SGI-STL将用户申请内存块向上对齐到了8的整数倍;(每个链表中一定有一个指针,在32位系统下以4 字节进行对齐,在64位系统下以8字节进行对齐)
    在这里插入图片描述
  • 空间申请分配流程:
    在这里插入图片描述
  • 向桶内填充内存块

在这里插入图片描述

  • 向内存池索要空间

在这里插入图片描述

  • 空间的回收
    在这里插入图片描述4、SGI-STL默认使用一级还是二级空间配置器取决于USE_MALLOC宏;因此默认情况下使用二级空间配置器

二、STL关联式容器总结
1、STL本质是什么?

STL是标准模板库,是c++高效的程序库,采用泛型编程思想对常见数据结构和算法进行封装,并被集成到c++标准库中去;
具体:
STL包含了容器、适配器、算法、迭代器、仿函数以及空间配置器;(STL的六大组件)

2、STL的基本算法

STL中的算法主要分为两大类:
与数据结构相关的算法、通用算法;
主要包含:排序、查找、排列组合、拷贝、删除、比较组合、运算等;

3、迭代器

  • 迭代器让用户通过特定接口访问容器数据而不需要了解容器的底层结构;(本质是一个指针
  • 为什么需要迭代器?
    因为一些通用算法(与具体数据结构无关),只需要给出待查找数据集合范围即可,所以产生了迭代器;
  • 迭代器的实现原理
    (1)vector
    其迭代器实际上是对原生指针的封装
typedef T* iterator;

(2)list
list的底层结构是双向循环链表,迭代器在移动时,只能按照链表的结构前后依次移动,所以要对原生指针进行封装,因为当迭代器++时,因该通过结点中的next指针域找到下一个结点;
过程:
a)重载*和->
b)重载++/–让迭代器可以移动
c)重载!=/==y运算符,支持比较并可以判断是否到达区间末尾;

4、适配器

将一个类的接口转换成用户希望的另一个类的接口,使原本接口不兼容的类可以一起工作。
比如:
stack和queue的底层容器为deque

5、仿函数

1、什么是仿函数?

仿函数是一种具有函数特征的对象,调用者可以像调用函数一样的调用该对象,且该对象必须自定义函数调用运算符()(重载小括号),即可在仿函数对象的后面加上一对小括号,以此调用仿函数所定义的operator()操作;

2、写法举例:

#include <vector>
#include <algorithm>
class Mul2
{
public:
void operator()(int& data)
{ data <<= 1;}
};
class Mod3
{
public:
bool operator()(int data)
{ return 0 == data % 3;}
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值