《C++Primer》第九章:顺序容器

  1. 元素在顺序容器中的顺序与其加入容器时的位置相对应。区别于关联容器,元素的位置由元素相关联的关键字值所决定。
  2. 顺序容器包括vectordequelistforward_listarraystring。其中array是固定大小,其他的都是可变容器。
  3. 对于选择容器:
  • vector通常来说是最好的默认选择。
  • 存放大量小的元素不推荐使用list(指针的额外空间消耗)。
  • 大量的随机访问使用vector或deque。
  • 大量的随机位置的添加删除使用list。
  • 如果不确定使用哪个,那么在程序中只使用迭代器;不适用下标。这样在需要更换的时候比较方便。
  1. 容器均被定义成了模板类。
  2. 迭代器是一组良好实现的接口,用于统一操作或者访问容器。
  1. 迭代器范围的概念是标准库的基础。迭代器范围是一对迭代器,构成左闭合区间。程序员需要确保:(1)两个迭代器指向同一个容器或其尾元素的后一个位置。(2)end大于等于start。当且仅当两个迭代器相等时,范围为空。好处是用不相等作为继续循环的条件,此时可以在循环体内安全的对start解引用。
  2. 反向迭代器的操作都是颠倒的,对反向迭代器进行++会得到上一个元素。
  3. 用begin举例。除了普通的迭代器,还包括带r的反向版本和带c的const版本。不以c开头的版本都是被重载过的,意思就是一个是const成员函数,返回const_iterator类型;还有一个同名非const成员函数,返回类型是iterator。因此调用begin的时候返回时是否是const的iterator取决于调用的对象是否是const的。而如果使用cbegin,返回的一定是const_iterator,因为非常量可以隐式转化为常量,但是反之不行。
  4. 顺序容器的拷贝构造,要求容器类型匹配。但是如果使用迭代器范围构造一个新的容器就不需要类型匹配了,容器中的类型可也以不同,只要可以隐式转换。
  5. 比较特殊的是array类型,他是一个不可变大小的容器,因此在定义的时候要显式的指定类型和大小,如array<int, 10>
  6. swap内置函数不对任何元素进行拷贝、插入、删除等操作,因此可以保证在常数时间内完成。因为元素本身没有交换,交换的只是两个容器自身的数据结构。所以swap之后,指针、引用和迭代器不会失效,还指向原来的位置,不过此时该位置属于另外一个容器。(但是对array和string进行swap会失效。)对于array,情况有所不同,swap会真正的交换他们的元素,因此时间复杂度是线性的。对于array,swap之后,元素值会改变。(swap的这种特性要求swap的两个对象类型一定要一致!)
  7. 非成员函数版本的swap在泛型编程中是非常重要的,统一使用非成员函数的版本是一个好习惯。
  8. 赋值运算符要求左边和右边的运算对象具有相同的类型,它将右边运算对象中的所有元素拷贝到左边运算对象中。除了array之外的顺醋容器还定义了一个assign的成员函数,允许我们从一个不同但相容的类型赋值,或者从一个容器的子序列赋值,assign的操作是用参数所指定的元素(的拷贝)替换左边容器中的所有元素
  9. 除了无需关联容器之外的所有容器都支持关系运算符,两边必须是相同元素的相同容器。比较原理同python。
  10. 如果向容器中加入元素比如insert或者push_back,实际上容器中的元素是拷贝,容器中的元素与提供的元素之间没有任何关系,即使向vector中追加一个vector,这一点和python是不一样的。
  11. insert函数是一个比较强大的函数,比如vector不支持push_front,但是大多数容器都支持insert,而且insert有多个重载版本,另外和push操作不同的是,insert有返回值,返回值是指向新插入的元素的迭代器。insert的第一个参数接受一个迭代器。
  12. c++11提供了emplace_front/emplace/emplace_back三个新函数,分别对应push_front/insert/push_back。区别不是使用拷贝构造,而是直接调用对象的构造函数。
  13. 访问容器中的元素推荐使用迭代器,使用迭代器一定要确保容器非空,否则和数组下标越界一样严重
  14. 下表访问推荐使用at,越界后会引发异常。(如果使用下标,编译不会出错,运行也不会,只不过得到的值是未定义的,而at会导致一个运行时错误。)
  15. 删除元素的成员函数有pop_backpop_fronteraseclear,其中pop系列类似于push系列,erase类似于insert,可以接受一个迭代器,也可以接受连个迭代器确定的迭代器范围。这几个方法除了clear都要确定容器非空。另外pop方法并不返回被删除的元素,而是返回void。
  16. 除了array,顺序容器可以使用resize改变大小。
  17. 迭代器失效,见315页,9.3.6节。
  18. 不要保存end返回的迭代器,因为vector和string增添元素或者对于deque首尾元素意外的位置增删元素都会使尾后迭代器失效。
  19. vector和string是动态表的实现。capacity成员函数告诉我们不扩张内存情况下可以容纳多少个元素,reverse(n)成员函数会分配至少容纳n个元素的空间,shrink_to_fitcapacity减少为和size同样大小(关系到具体底层实现,底层可能忽略该函数)。
  20. 构造string的函数很类似于bash,比如string s(s2,pos)等价于s=${s2:pos}string s(s2,pos2,len2)等价于s=${s2:pos2:len2}。其中len2的长度就算特别长,s也只会拷贝到s2的末尾。
  21. string有一系列全局函数可以进行数值转换,推荐使用stoi系列,字符串非法时会报错,另外接手的参数类型是string。而atoi接受的参数类型是char*,而且字符串不能转换时也不会报错,会返回0。对于stoi接受的字符串,第一个非空白符一定要是正负号或数字,可以用0x开头表示十六进制数字,对于stod函数,也接受小数点开头的字符串。
  22. 除了顺序容器,标准库定义了三个顺序容器适配器stackqueuepriority_queue。适配器是标准库中的一个通用概念,容器、迭代器、和函数都有适配器。本质上适配器是一种机制,使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像是一种不同的类型。默认情况下,stack和queue是基于deque的,priority_queue是基于vector的,也可以自行指定底层容器。

  23. stack的操作:pop、push、emplace、top。

  24. queue的操作:pop、front、back、top、push、emplace。
  25. priority_queue根据元素的<运算符确定优先级。
本站总访问量