- 三种顺序容器:vector、list、deque
区别: 访问元素的方式以及添加或删除元素相关操作的运算代价.
vector: 支持快速随机访问(即任意访问,与磁盘、磁带等需要寻道、倒带访问区分)
list: 支持快速插入/删除
deque: 双端队列
适配器: 根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型.
顺序容器适配器: stack、queue、priority_queue
2. 内置类型、复合类型与类类型
内置类型:编译器内置的基本类型,如int, char, float, double, bool等;
复合类型:根据其它类型定义的类型,主要有这么几类:数组,字符串(C-style),指针,引用,结构体(struct),联合体(union);
类类型:用struct和class定义的类。
- 容器构造函数
接受容器大小作为形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化
// 容器类型名C,元素类型T,空容器c
C<T> c;
// 创建容器c2的副本c,c和c2必须容器类型相同、元素类型相同
C c(c2);
vector<int> ivec2(ivec);
// c容器元素为迭代器b和e标示的范围内的副本,左闭后开
C c(b, e);
// n个值为t的元素创建容器c,t的类型必须是容器C的元素类型的值
C c(n, t);
// 创建有n个值初始化元素的容器c
C c(n);
- 容器内元素的类型约束
- 容器元素类型必须满足以下两个约束:
元素类型必须支持赋值运算、元素类型的对象必须可以复制. - 除引用类型外,所有内置和复合类型都可以用作元素类型.因为引用不支持一般意义上的赋值运算.
- 除IO标准库类型(及auto_ptr类型)外,所有其它标准库类型都是有效的容器元素类型.因为它不支持复制和赋值.
- 容器的容器
// 对
vector<vector<string> > lines;
// 错,两个尖括号必须中间用空格分开,否则会被认作右移操作符
vector<vector<string> > lines;
- list不支持算术运算和关系运算
// list是双向链表结构,不支持算术运算(加法或减法),也不支持关系运算(<=,<,>=,>),它只支持前置和后置的自增、自减运算以及相等(不等)运算.而vector和deque容器的迭代器支持
list<int> ilist(vec.begin(), vec.end());
ilist.begin() + ilist.size()/2; //错
- 顺序容器中添加元素的操作
c.push_bach(t);
// 在c的前端添加值为t的元素,返回void类型.只适用于list和deque,vector不适用
c.push_front(t);
// 在迭代器p所指向元素前面添加值为t的元素,返回指向新添加元素的迭代器
c.insert(p,t);
// 在迭代器p所指向元素前面添加n个值为t的元素,返回void类型
c.insert(p,n,t);
// 在迭代器p所指向元素前面添加由迭代器b和e标记的范围内的元素,返回void
c.insert(p,b,e);
- 容器大小的操作
// resize操作可能会使迭代器失效,如果resize操作压缩了容器,则指向已删除的元素的迭代器失效.
c.size();
c.empty();
// 返回容器c可容纳的最多元素个数,返回类型为c::size_type
c.max_size();
//调整容器的大小,大小为n,如果n<c.size(),则删除多出的元素,否则,添加值初始化的新元素
c.resize(n);
// 调整容器大小,大小为n,所有新添加的元素值都为t
c.resize(n,t);
- 访问元素
// 返回容器c的最后一个元素的引用
c.back();
// 返回容器c的第一个元素的引用
c.front();
// 返回下标为n的元素的引用,如果n<0或n>=c.size(),则该操作未定义,运行错误
c[n];
// 返回下标为n的元素的引用,如果n<0或n>=c.size(),则该操作未定义,抛出out_of_range异常
c.at(n);
- 删除元素
// 删除迭代器p所指向的元素,返回被删除元素后面的元素,如果p指向最后一个元素,则返回最后一个元素的下一个位置,如果p指向最后一个元素的下一个位置,则该函数未定义
c.erase(p);
// 删除迭代器b和e所标记范围内的所有元素(不包括e指向的元素),返回一个迭代器,指向被删除元素段后面的元素,如果e指向最后一个元素的下一个位置,则返回的迭代器也指向最后一个元素的下一个位置
c.erase(b,e);
// 删除容器内的所有元素,返回void
c.clear();
// 删除容器的最后一个元素,返回void,如果c未空容器,则该函数未定义
c.pop_back();
// 删除容器的第一个元素,返回void,如果c未空容器,则该函数未定义,只适用于list和deque容器(这两个都是基于链表实现的),删除并不是vector的强项,它的作用是随机存取,如果删除第一个元素,则应该后面的所有元素的下标位-1,即c[1]编程c[0],对于随机存取来说,太过繁琐.
c.pop_front();
//erase、pop_front和pop_bach函数使指向被删除元素的所有迭代器失效,对于vector容器,指向删除点后面的元素的迭代器通常也失效,而对于deque容器,如果删除时不包括第一个元素或者最后一个元素,那么该deque容器相关的所有迭代器都会失效.
- 赋值与swap
如果在不同(或相同)类型的容器中,元素类型不同,但是相互兼容,则其赋值运算必须使用assign函数,而不能使用复制操作符=,如果容器类型和容器中元素类型都相同,则可以使用复制操作符=.
// 删除容器c1中的所有元素,然后将c2的元素复制给c1,c1和c2的容器类型和容器元素类型必须相同
c1 = c2;
// 容器c1中存放的是容器c2的元素,容器c2中存放的是原来容器c1的元素,该函数的执行速度(常量时间)比复制操作快.迭代器不会失效,原来的迭代器还是指向原来的元素.
c1.swap(c2);
// 重新设置c的元素,将迭代器b和e标记的范围内的所有元素复制到c中,b和e必须不是指向c中元素的迭代器,左操作数迭代器失效.
c.assign(b,e);
// 将容器c重新设置为存储n个值为t的元素,左操作数迭代器失效.
c.assign(n,t);
- vector容器的自增长
- vector容器为了支持快速的随机访问,容器中的元素以连续的方式存放,即每一个元素都紧挨着前一个元素存储.
- 当向容器中添加一个元素时,如果容器中没有空间容纳新的元素,则vector必须重新分配存储空间,用来存放原来的元素以及添加新的元素,存储在旧存储空间中的元素会被复制到新存储空间中,接着插入新元素,最后撤销旧的存储空间.
- 为了使vector实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些
- capacity和reserve成员
capacity操作获取在容器需要分配更多的存储空间之前能够存储的元素总数.而size是容器当前拥有的元素个数.
reserve操作告诉vector容器应该预留多少个元素的存储空间.
ivec.reserve(50);
// 50
ivec.capacity();
- 容器的选用
- vector和deque容器提供对元素的快速随机访问,代价是在容器的任意位置插入或删除元素(需要移位),比在容器尾部插入和删除的开销更大.
- list类型在任何位置都能快速的插入和删除,代价是元素的随机访问开销大.
- deque双向队列是一种双向开口的连续线性空间,可以高效的在头尾两端插入和删除元素,deque在接口上和vector非常相似.
- 与vector容器一样,在deque容器中insert和erase元素的效率比较低
- 不同于vector,deque容器提供高效的在其首部实现insert和erase的操作,就像在容器尾部一样
- 与vector容器一样而与list容器不同的是,deque容器支持对所有元素的随机访问
- 在deque容器首部和尾部插入元素不会使任何迭代器失效,而在首部或尾部删除元素则只会使指向被删除元素的迭代器失效,在deque容器的任何其它位置插入和删除操作将会使指向该容器元素的所有迭代器失效.
- 构造string对象的方法
// 创建一个string对象,它被初始化为cp所指向数组的前n个元素的副本
string s(cp, n)
// 创建一个string对象,它被初始化为一个已存在的string对象s2中从下标pos2开始的字符的副本,如果pos2>s2.size(),则该操作未定义
string s(s2, pos2)
// 创建一个string对象,它被初始化为一个已存在的string对象s2中从下标pos2开始的len2个字符的副本,如果pos2>s2.size(),则该操作未定义
string s(s2, pos2, len2);
string s1;
// "aaaaa", 注意与string s(s2, pos2)的区别
string s2(5, 'a');
string s3(s2);
// "aa"
string s4(s3.begin(), s3.begin() + s3.size() / 2);
- string类型不同于容器类型的几种操作
// insert、assign、erase等操作与vector等容器基本相同
// 返回从字符串s中从pos位置开始n个字符的字符串
s.substr(pos,n);
// 返回从字符串s中从pos位置开始直到结束位置的字符串
s.substr(pos);
// 返回s的副本
s.substr();
s.append(args);
// 删除s中从pos位置开始的len个字符,用args指定的字符替换之,返回s的引用,args不能是b2,e2
s.replace(pos,len,args);
// 删除迭代器b到e范围内的所有字符,用args替换之,返回s的引用,args不能是s2,pos2,len2
s.replace(b,e,args)
操作的参数args:
s2 // 字符串s2
s2, pos2, len2 // 字符串s2中从下标pos2开始的len2个字符
cp // 指针cp指向的以空字符结束的数组
cp, len2 // 指针cp指向的以空字符结束的数组中前len2个字符
n, c // 字符c的前n个副本
b2, e2 // 迭代器b2和e2标记的范围内所有字符
// 在s中查找args的第一次出现
s.find(args);
// 在s中查找args的最后一次出现
s.rfind(args);
// 在s中查找args的任意字符的第一次出现
s.find_first_of(args);
// 在s中查找args的任意字符的最后一次出现
s.find_last_of(args);
// 在s中查找第一个不属于args的字符
s.find_first_not_of(args);
// 在s中查找最后一个不属于args的字符
s.find_last_not_of(args);
操作的参数args:
c, pos //在s中,从下标pos开始查找字符c,pos默认0
s2, pos // 在s中,从下标pos开始查找string对象s2,pos默认0
cp, pos // 在s中,从下标pos开始查找指针cp所指向的C风格的以空字符结束的字符串,pos的默认值为0
cp, pos, n // 在s中,从下标pos开始查找指针cp所指向的数组的前n个字符,pos和n都没有默认值
// 比较s和s2
s.compare(s2);
// 让s中从pos1下标开始的n1个字符与s2比较
s.compare(pos1, n1, s2);
// 让s中从pos1下标开始的n1个字符与s2中从pos2开始的n2个字符比较
s.compare(pos1, n1, s2, pos2, n2);
// 比较s和指针cp所指向的C风格的以空字符结束的字符串
s.compare(cp);
// 比较s中从pos1下标开始的n1个字符和指针cp所指向的C风格的以空字符结束的字符串
s.compare(pos1, n1, cp);
// 比较s中从pos1下标开始的n1个字符和指针cp所指向的C风格的以空字符结束的字符串的前n2个字符
s.compare(pos1, n1, cp, n2);
- 容器适配器
除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue和stack适配器.适配器是标准库中通用的概念,包括容器适配器、迭代适配器和函数适配器.本质上,适配器是使一种事物的行为类似于另一事物的行为的一种机制.容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现.例如,stack适配器可使任何一种顺序容器以栈的方式工作,默认stack和queue建立在deque容器上,而priority_queue建立在vector容器上.