本节主要总结模板及其类模板分类以及STL里面的分配器、容器内部结构以及容器之间的关系和分类,还介绍了容器中List的结构分布
1、源代码版本介绍
1.1 VC的编译器源码目录:
2、类模板
2.1 类模板简介:
2.2 类模板分类:
泛化(GP Generic Programming),特化(Specialization),二者区别在于特化版本相比泛化版本可能存在效率上的优化
2.3 偏特化(局部特化 Partial Specialization)
- 特化又叫做全部特化,上面的例子中类模板参数只有一个,其实可以由多个,这个全部特化就是对所有的参数都进行特殊绑定;
- 偏特化叫做局部特化,下面例子对于多个参数只绑定其中一部分(局部数量或者局部范围内的绑定)。
3、函数模板
3.1 函数模板简介:
4、 分配器
4.1 分配内存实质
- 分配器是容器的幕后英雄,负责内存的使用,但是不建议用户直接使用它,因为没有需要用它,它是专门为容器服务的,了解分配器可以更好的掌握容器的操作效率,方便我们挑选合适的容器;
- 分配器操作内存的实质还是调用C Runtime Library的malloc和free(可以说c++的new以及任何其他上层操作内存的方式最终一层层调用都是回归到malloc和free这两个函数上),这两个函数再根据不同平台再去调用各自的System API。
- 系统申请的内存如下
所以每次申请的内存后系统分配给我们的远远大于我们实际申请的空间,因为系统要给这块内存附加一定的标识(也可叫做cooking或者额外开销),以方便释放操作,因为附加的部分是固定的,也就说我们申请的内存越大,附加部分占用的比重就越小。
4.2 分配器源码实现
注意:如果我们每次放入容器的元素太小,比如放一个long(4个字节),那内存产生的额外的开销将会很大,如果放入100万个,那额外开销可能比你放的100万个long还要多,内存消耗将会很可怕的。
4.3 分配器的使用
由此可见,分配器的使用在释放时需要我们自己去记得当初申请了多少内存,显然是不方便的,但是我们在使用容器时不用关心,结论就是个人慎用。
4.4 分配器工作模式
用分配器而不直接用C RunTime Library提供的malloc函数,其本质上是减少内存的额外开销,也就是说分配器会预先开好一定的空间,尽量减少malloc的使用次数,它不是需要多少开多少,也就是我们常说的内存池设计。
5、 容器的结构分类介绍
5.1 各个容器的结构关系
容器间的关系,以及各个容器所占用的字节大小如下所示
5.2 List结构分析
5.2.1 基本结构
G2.9版本,list的设计是双向环状链表,每个节点是一个指针,这个节点又由两个指针(一个向前指一个向后指)和数据部分data组成;
list整体数据部分是node,而node是list_node*类型,list_node是list的一个节点,所以list占用的内存大小是4(在32位机器上),当list从allocate拿内存时是以节点为单位的,也就是说除了数据data部分,还要有两个指针;
5.2.2 迭代器操作方式
迭代器的操作要回到节点内部,而不是直接跳到下一个地址,比如迭代器++操作,就是要回到节点内部找到next指针指到的位置,然后再去挪动迭代器,迭代器的操作,都是一堆运算符重载
注意:c++语法不允许后++加两次,因为后加加操作符重载后返回的不是引用(后加加里面有临时变量)
5.2.3 多版本对比介绍
注意:最新版本中,list数据部分(node节点)拆成了两个指针,list大小为两个指针的大小,为8(32位系统下)