- 实在有点难读下去,第一次读就读得比较粗糙了。
第1章 概论与版本简介
- 为建立数据结构和算法的一套标准,并且降低其间的耦合关系,以提升各自的独立性、弹性、交互操作性,C++社群里诞生了STL。
- STL是一个抽象概念库,这些抽象概念包括:
①最基础的:可被赋值、不需要任何参数就可构造、可判断是否相同、可比较大小、正规。
②高阶一点的:具输入功能的迭代器、具输出功能的迭代器、单向迭代器、双向迭代器、随机存取迭代器、一元函数、二元函数、传回真假值的一元判别式、传回真假值的二元判别式。
③更高阶的:序列式容器、关联式容器。
- STL的六大组件
①容器(containers):各种数据结构,如vector、list、deque、map等。实现上看是一种class template。
②算法(algorithms):各种常用算法,如sort、search、copy等。实现上看是一种function template。
③迭代器(iterators):容器与算法之间的胶合剂,一种“泛型指针”。将*、->、++、--等指针相关操作重载的class template。所有STL容器都附带有自己专属的迭代器。
④仿函数(functors):行为类似于函数,可以作为算法的某种策略。
⑤配接器(adapters):一种用来修饰容器或仿函数或迭代器接口的东西。像queue、stack,看起来像容器,其实只是一种容器配接器。
⑥配置器(allocators):负责空间配置与管理。一个实现了动态空间配置、空间管理、空间释放的class template。
- 由于STL已成为C++标准程序库的大脉系,所以目前所以的C++编译器一定有一份STL。
- STL并非以编译好的二进制代码出现,而是以源代码提供给用户。
- 因为C++标准规定,所以标准头文件不再有扩展名。但某些STL版本同时具有扩展名和无扩展名两份文件(可能是为了向下兼容、或者内部组织管理)。
- 任何一个STL算法,都需要获得由一对迭代器标示的区间,来表示操作的范围。而这个区间是[first,last)表示。也就是说实际范围是从first开始,到last-1结束。
- 在C时代,要将函数当参数传递,只能通过函数指针。但函数指针有缺点,它无法拥有自己的“状态”,也就是说没法再次将一些修饰条件加于其上、改变其状态。
- 为此,STL算法接受所谓的“策略”或“一组操作”,以仿函数的形式呈现。
- 仿函数:使用起来和函数一样,如果针对某个class进行operator()重载【即重载()符号】,它就成为一个仿函数。
#include<iostream>
using namespace std;
//将operator()重载,使plus成为一个仿函数
template<class T>
struct plus{
T operator()(const T& x, const T& y)const { return x + y; }
};
int main() {
//产生一个仿函数对象
plus<int> test;
//使用仿函数,就和使用普通函数一样
cout << test(3, 5) << endl;
//也可以产生一个临时对象,并且调用
cout << plus<int>()(3, 5) << endl;
getchar();
return 0;
}
第2章 空间配置器(allocator)
- 叫做空间配置器,而不是内存配置器。因为除了内存,空间还是可以是磁盘或者其他存储介质。
- 为精密分工,STL allocator将对象构造析构和内存分配释放两个阶段操作区分开(new delete时是合并的)。
- 内存配置操作:alloc::allocate()
内存释放操作:alloc::deallocate()
对象构造操作:::construct()
对象析构操作:::destroy()
第3章 迭代器
- 迭代器是一种行为类指针的对象。迭代器最重要的编程工作就是对operator*和operator->重载。
第4章 序列式容器
- 依据“数据在容器中的排列”特性,容器的数据结构分为序列式(sequence)和关联式(associative)两种。
- 序列式容器:array(C++内建)、vector、heap(以算法形式呈现xxx_heap)、priority-queue、list、slist、deque、stack(配接器)、queue(配接器)。
- heap内含一个vector,priority-queue内含一个heap,stack和queue都内含一个deque。
- 关联式容器:RB-tree(非公开,衍生出后面几种):set、map、multiset、multimap。(后面的是非标准)hashtable、hash_set、hash_map、hash_multiset、hash_multimap。
- vector和array的区别在于vector空间运用的灵活性。array是静态空间,一旦配置了就不能改变。而vector是动态空间,随着新元素加入,内部机制会自动扩充空间。
- vector采用的是:线性连续空间。内部以start和finish两个迭代器来指向配置得来的连续空间中,目前已使用的范围;以迭代器end_of_storage指向整块空间的尾端。
- 按照书中说法,vector每当遇到空间不足的时候,会以2倍增长,也就是说:2→4→8→16等等。但在VS中实际测试的数据却是:2→3→4→6→9→13→19→28→42,整体计算是以1.5倍的速度增长。然后我就又进了gcc去看,确实是2倍速率增长的。
为啥是1.5和2呢?显然,增长的倍数不可能很大,也不会比 1 小,那么,它的最佳上限是多少呢?如果以 大于2 倍的方式扩容,下一次申请的内存会大于之前分配内存的总和,导致之前分配的内存不能再被使用。所以,最好的增长因子在 (1,2)之间。然后好像有谁实验证明1.5是最佳值,就有了1.5,具体怎么证明就不太清楚了,大概知道就好。 - vector可能存在旧空间不足,后面也没有合适的空间,于是会重新配置一块更大的空间,然后复制元素,再释放旧空间。
- list结点的结构体:
template <class T>
struct __list_node{
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
}
- list的插入操作和结合操作,都不会造成原有的list迭代器失效,而在vector中是会的。
- deque和vector差异:①deque可以在常数时间内对头端进行元素的插入或移除。②deque没有所谓的容量概念,因为它是动态地以分段连续空间组合而成。
- deque采用了一块所谓的map(不是STL的map)来实现。这里的map是一小块连续空间,其中每个结点都是一个指针,指向另一段较大的连续线性空间,也就真正的存储主体,称为缓冲区。一旦map所提供的结点不足,会重新分配一块更大的map。
- stack和queue的内部都是默认以deque实现的。同样,stack和queue都没有迭代器。
第5章 关联式容器
- 标准STL关联式容器为set和map两大类。这两大类的衍生体有multiset和multimap。这些容器的底层机制均以RB-tree红黑树实现。RB-tree也是一个独立的容器,但不开放给外界使用。
- set和map不一样之处是,map有实值value和键值key,而set只有一个值,并且不允许两个元素有相同的键值。set中所有元素都会根据元素键值自动被排序。
- map会根据元素的键值自动排序。