容器和算法
1、顺序容器(sequential container),元素按其位置存储和访问。
标准库定义了三种顺序容器类型:vector、list、deque(double-ended queue)。
它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价。
顺序容器适配器:stack、queue、priority_queue
2、关联容器,元素按键(key)排序。
关联容器类型:map、set、multimap、multiset
3、为容器类型提供通用接口是设计库的目的。
顺序容器
1、顺序容器的定义以及初始化
类模板是对一批仅仅成员数据类型不同的类的抽象,程序员只要为这一批类所组成的整个类家族创建一个类模板,给出一套程序代码,就可以用来生成多种具体的类,(这类可以看作是类模板的实例),从而大大提高编程的效率。
容器元素的初始化,基本就是讲解默认构造函数以及其他的构造函数。
NOTE:通过传递一对迭代器,可实现复制不能直接复制的容器。(类型可不同,但要兼容)
2、容器内元素的类型约束:支持赋值运算;必须可以复制。
没有引用、IO库类型的容器(不支持复制或赋值)
3、容器的容器(use "> >"not">>")
4、迭代器为所有标准库容器类型提供了常用的运算。但是,却为vector和deque容器提供额外的算术和逻辑运算。因为这两种容器都支持通过元素位置实现的随机访问。
5、迭代器范围(iterator range)
因为编译器无法知道迭代器所关联的是哪个容器并且也不知道容器内有多少个元素,所以对形成迭代器范围的迭代器有两点要求:
1)指向同一个容器中的元素或超出末端的下一位置
2)last绝对不能位于first之前
PS:为了利于循环,使用左闭合区间([ first,last ))
6、无法检查迭代器是否有效,也无法通过测试来发现。所以要特别注意使迭代器失效的操作(元素添加或删除等)!类似的还有悬垂指针。
7、不要忘记容器的rbegin和rend操作(返回reverse_iterator类型)。
8、insert函数是在其指向位置之前而非之后插入元素;并返回指向新插入元素的迭代器(可实现指定位置重复插入)。
9、避免存储end操作返回的迭代器(添加或删除容器内的元素会导致存储的迭代器失效)。
解决方法:在每次做完插入或删除运算后重新计算end迭代器的值。
10、容器的比较是基于容器内元素的比较,所以不仅要有相同的容器类型,也要有相同的元素类型(并且元素类型要支持相应的关系操作符)。
ps:C++语言只允许两个容器做其元素类型定义的关系运算。
11、赋值和assign操作使左操作数容器的所有迭代器失效;swap操作则不会使迭代器失效(但是迭代指向的已经不是原来的容器中的元素)。
PS:由于assign操作首先删除容器中原来存储的所有元素,因此传递给assign函数的迭代器不能指向调用该函数的容器内的元素。
12、标准库的内存分配策略:以最小的代价连续存储元素,借此带来的访问元素的便利来弥补存储代价。
为了使vector容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。导致结果就是,对于大部分应用,使用vector容器是最好的。
13、vector容器处理内存分配的细节是其实现的一部分。(capacity和reserve成员)
14、对容器的操作(插入删除、随机访问)的使用程度决定了程序选择哪一类容器。
15、再谈string类型
1)支持大多数顺序容器操作,但不支持以栈方式(push、pop、front、back)操纵容器。
2)string库定义了大量使用重复模式的函数。需要某种操作时再细读即可。
16、容器适配器queue、priority_queue、stack
对于给定的适配器(三者之一),其关联的容器(vector、list、deque之一)必须满足一定的约束条件(因为要实现适配器的一些功能,只有特定的容器才有)。
stack可以建立在vector、list或deque;queue只能建立在list上;priority_queue可以建立在vector或deque上。
关联容器(map容器是键-值对的集合,set是键的集合)
1、pair类型(也是一种模板类型)
2、对于键类型,唯一的约束就是必须支持<操作符
3、map定义的类型有三种:map<K,V>::value_type;map<K,V>::key_type;map<K,V>::mapped_type。
map迭代器返回pair类型的对象(map<K,V>::value_type);map下标返回特定键所关联的值(map<K,V>::mapped_type)。
PS:若访问map中不存在的元素将导致添加一个新的元素,这样的特性使得编程惊人地简练(相对于insert)。
4、给map容器添加新元素有两种方式:下标方式和insert方式。
PS:下标方式过程为先初始化然后赋值;而直接使用insert成员则避免了不必要的初始化,缺点是实参笨拙(可以用make_pair或typedef简化)。
但应该理解的是:insert本质是插入,如果已存在则不做任何操作;下标方式本质是赋值。
5、检查map对象中某键是否存在,map容器提供了两个操作:count和find,用于检查某个键是否存在而不会插入该键。
6、set容器支持大部分的map操作(不支持下标操作符,而且没有定义mapped_type类型)
7、multimap和multiset类型允许一个键对应多个实例。操作与map和set相同,只有一个例外(multimap不支持下标操作)
8、multimap和multiset容器中,如果某个键对应多个实例,则这些实例在容器中奖相邻存放。
9、查找元素:由find和count到lower_bound、upper_bound和equal_range
10、容器的综合应用:文本查询程序
泛型算法(generic algorithm)
”泛型“指的是它们可以操作在多种容器类型上;“算法”指的是它们实现共同的操作
1、这些算法从不使用容器操作,因此其实现与类型无关,元素的所有访问和遍历都通过迭代器实现。
2、从三个最基本的方面理解算法:是否对元素进行了读、写或者重新排序。(算法从不直接添加或删除元素,如需则使用容器操作)
find,accumulate,find_first_of
fill,fill_n,back_inserter,copy,replace,replace_copy
sort,unique,stable_sort,count_if
3、插入迭代器、iostream迭代器、反向迭代器(需要时查看即可)
4、泛型算法的结构(形参模式、命名规范)
1)带有一个值或一个谓词函数参数的算法,不能重载只能提供另外命名的版本(如find,find_if),因为它们带有相同数目的形参。
5、由于容器的内部结构,为了更高效地执行,应该优先使用容器特有的算法而不是泛型算法。