《stl源码剖析》第二章学习记录

这里记录一下学习第二章空间配置器章节的学习心得。

2.1一开始给出了一个空间配置器的必要接口,其中allocate(空间的申请),deallocate(空间的释放),construct(在申请的空间上进行对象构造),destory(在申请的空间上将对象析构掉)是后面文章重点介绍的。

        2.1.1实现了一个简单的空间配置器,我们看到这个简易配置器对allocate 与 deallocate的实现很简单,仅仅是对全局new 与 delete的简单调用,对construct与destory的实现也很简单,仅仅是做了局部new 以及简单调用析构函数。显然,这个简单版本是无法满足各种容器的需求的。

2.2 一开篇介绍了stl各种容器模板第二个参数就是空间配置器类型,只不过定义了一个默认的空间配置器,所以我们平常使用vector map 等等容器时一般并不显式指定空间配置器,使用的是默认的。学到了!

        2.2.1介绍了SGI的一个效率不佳的空间配置器版本,std::allocator,我们发现这个版本和2.1.1中作者自己的版本大差不差,虽然空间配置器必备的接口都有,但是效率不佳的原因同上,也仅仅是对new 与 delete 的简单封装。

        2.2.2以一个例子介绍了allocate,deallocate,construct,destory四个行为的先后关系,这也是为什么配置器类提供这四个接口的原因,就是要将每一个行为具体化,各司其职。stl在三个头文件中实现了主要功能。stl_construct.h定义了全局函数construct,destory,这两个函数主要给allocator中的接口去调。stl_alloc.h定义了一二级配置器类,无论是一级还是二级,都必须有一开始提到的配置器必备的接口。stl_uninitialized.h定义了一些内存操作的函数,类似于memcpy,主要是进行高效率的内存操作。

        2.2.3介绍了stl_construct.h中 construct,destory的实现,我们发现相较于前面简单的对new与delete的封装,这里为delete做了更多的模板特化处理,如果一个对象有默认的析构函数,那么我们就在destory时直接什么也不做,当对象生命期结束时自动调用其默认的析构函数,如果没有默认的析构函数,直接显式的调用该对象的析构函数。并且提供了一些对char* wchar*的特化版本处理。

        2.2.4介绍了stl_alloc.h中空间配置器类的实现思想。主要思想是一个双层级配置器,以128B为界限,通过预编译命令决定只使用第一级还是同时开放第二级配置器,并将他们都命名为alloc。simple_alloc类对alloc进行了一个简单的包装,提供了四个接口allocate,deallocate,construct,destory,这四个接口的实现都是调用alloc中的实现,换言之,实现这个简单包装的目的就是为了满足一开始的配置器接口准则。2.2.4末尾以deque与vector为例给出了在容器模板内部到底该如何使用配置器,还蛮有意思的。

        2.2.5给出了第一级配置器的实现,这里的逻辑是如果我们正常的内存申请allocate ,再申请reallocate,因为内存不够做不下去了,我们会调用oom版本,在两个oom版本中都会首先调用my_malloc_handler,这个函数指针是模板类的静态数据成员,初值为NULL,并且其函数实现等待客户自己指定,换言之,我们可以制定一些行为去回收内存。两个oom版本内部就是一个无限循环,不停地试图重新申请空间。我们发现这里的申请逻辑还挺有意思的,主要是赋予用户了一些回收内存的责任。

        2.2.6介绍了第二级配置器的思想。开篇首先指出了第一级配置器在频繁申请小内存时的问题,两点,第一:外部碎片 第二:因为管理内存所带来的额外的开销。所以为面对小内存申请的问题,第二级配置器出现了。第二级配置器维护了一个16级链表,每个链表代表的块数都是8的倍数,最大128B,并且对用户申请的内存大小做向上取整到8的倍数。并使用一个union来作为链表节点。第二级配置器的接口给出了,除了一些配置器必备接口,free_list是一个链表头结点数组,大小为16,refill做重新填充动作,chunk_alloc是配置一大块区间,start_free 与end_free两个字符指针框定了内存池的大小,heap_size是一个标记值,这个值只增不减。另外,这一小节使用了volatile关键字,有必要了解一下。

        2.2.7给出了第二级配置器中allocate的实现,主要思想就是根据申请大小定位到散列表的头结点,如果针对这条链表,如果链表有节点,就直接从头pop出一个,如果没有,通过refill为该链表填充一些节点,然后再从头pop出一个。

        2.2.8给出了第二级配置器中deallocate的实现,逻辑同上,直接头插法到链表头。

        2.2.9给出了第二级配置器中refill的实现,也就是重新填充动作,具体思想是调用chunk_alloc,然后获得nobjs(默认是20,实际个数不一定)个节点,然后留一个给用户取用,剩下nobjs-1个使用快慢指针插到该链表。相当于补充资源。

        2.2.10给出了chunk_alloc的实现,也就是大块申请动作。首先计算出内存池剩余容量与申请容量,如果剩余容量富足,就从内存池里取容量,如果内存池容量不够,但是够一个内存块,就修改nobjs为实际值,默认值是20,并将这一个内存块返回给用户。如果内存池容量连一个内存块都不够了,我们就需要从堆通过malloc取一些空间出来来补充内存池与16条链表的容量。首先先把内存块的残存容量找一个链表回收了。然后通过malloc申请空间,如果堆此时空间也紧张,那就采取极限措施,我们从用户申请内存块大小为起始,128B为终点,8B为步长遍历链表,逐个头结点pop一个到内存池,然后递归调用chunk_alloc,希望这些链表能够挤一挤给用户一个可用空间出来。如果这些链表都挤不出来了,那就调用第一级配置器的allocate,那里有我们客户自己设定的函数指针去回收内存,如果还是不行,那就抛出异常。我们发现,第二级配置器做了很多精细的操作。

2.3 介绍了stl_uninitialized文件中三个函数,uninitialized_copy uninitialized_fill uninitialized_fill_n.其实这三个函数模板的实现思想都是一样的,主要是根据对象是否是POD,如果是,就直接做最有效率的操作,如果不是,就调用construct。这里运用了萃取技术,下一章介绍这个技术。

总结:我们发现效率始终是贯穿在stl一以贯之的核心思想,配置器针对提高效率做了很多操作,如果我们不关注效率,其实一开始配置器的简单版本就已经可以解决问题了。这里我的理解是,如果一个对象有默认的构造析构函数,我们就可以使用类似内存操作memcpy的手段一样去直接进行操作,但是如果我们没有,就必须一个一个对象遍历过去取构造析构,这样做是很麻烦的,并且函数传参方面会有很多开销。

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
侯捷的《STL源码剖析》是一本关于STL(标准模板库)的学习笔记。这本书深入解析了STL的实现原理和设计思路,对于理解STL的内部机制和使用方法非常有帮助。这些学习笔记记录了作者在学习侯捷的《STL标准库和泛型编程》课程时的心得和总结,对于理解STL源码和进行泛型编程都具有指导意义。 这本书涉及了STL的各个模块,包括容器、迭代器、算法等,并解释了它们的实现原理和使用方法。通过学习这本书,你可以更好地理解STL的底层实现和使用技巧。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STLSourceAnalysis:stl原始码剖析(侯捷)的学习笔记](https://download.csdn.net/download/weixin_42175776/16069622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [候捷老师STL源码剖析视频课程笔记](https://blog.csdn.net/weixin_46065476/article/details/125547869)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [侯捷——STL源码剖析 笔记](https://blog.csdn.net/weixin_45067603/article/details/122770539)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值