本系列博客主要是在学习 C++ Primer 时的一些总结和笔记。
【C++ Primer 学习笔记】: 容器和算法之【顺序容器】
本文地址:http://blog.csdn.net/shanglianlm/article/details/49888699
顺序容器
1 定义
头文件
#include <vector>
#include <list>
#include <deque>
1-1 初始化
接受容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化。
1-2 容器内元素的类型约束
C++ 语言中,大多数类型都可用作容器的元素类型。容器元素类型必须满足以下两个约束:
• 元素类型必须支持赋值运算。
• 元素类型的对象必须可以复制。
引用不支持一般意义的赋值运算,因此没有元素是引用类型的容器。
除输入输出(IO)标准库类型之外,所有其他标准库类型都是有效的容器元素类型。
容器的容
// note spacing: use ">>" not ">>" when specifying a container element type
vector< vector<string> > lines; // vector of vectors
2 迭代器和迭代器范围
2-1 迭代器操作
vector 和 deque 容器的迭代器提供额外的运算
2-2 迭代器范围
左闭合区间
3 顺序容器的操作
3-1 容器定义的类型别名
3-2 begin 和 end 成员
3-3 顺序容器中添加元素
避免存储 end 操作返回的迭代器
不要存储 end 操作返回的迭代器。添加或删除 deque 或 vector 容器内的元素都会导致存储的迭代器失效。
导致死循环的错误示例
例如,考虑一个读取容器中每个元素的循环,对读出元素做完处理后,在原始元素后面插入一个新元素。
vector<int>::iterator first = v.begin(), last = v.end(); // cache end iterator
// diaster: behavior of this loop is undefined
while (first != last) {
// do some processing
// insert new value and reassign first, which otherwise would be invalid
first = v.insert(first, 42);
++first; // advance first just past the element we added
}
为了避免存储 end 迭代器,可以在每次做完插入运算后重新计算 end 迭代器值:
// safer: recalculate end on each trip whenever the loop adds/erases elements
while (first != v.end()) {
// do some processing
first = v.insert(first, 42); // insert new value
++first; // advance first just past the element we added
}
3-4 关系操作符
C++ 语言只允许两个容器做其元素类型定义的关系运算。
所有容器都通过比较其元素对来实现关系运算: ivec1 < ivec2
假设 ivec1 和 ivec2 都是 vector 类型的容器,则上述比较使用了内置 int 型定义的小于操作符。如果这两个 vector 容器存储的是 strings 对象,则使用 string 类型的小于操作符。
如果上述 vector 容器存储 Sales_item 类型的对象,则该比较运算不合法。因为 Sales_item 类型没有定义关系运算,所以不能比较存放 Sales_items 对象的容器:
3-5 容器大小的操作
3-6 访问元素
// check that there are elements before dereferencing an iterator or calling front or back
if (!ilist.empty()) {
// val and val2 refer to the same element
list<int>::reference val = *ilist.begin();
list<int>::reference val2 = ilist.front();
// last and last2 refer to the same element
list<int>::reference last = *--ilist.end();
list<int>::reference last2 = ilist.back();
}
3-7 删除元素
3-8 赋值与 swap
赋值和 assign 操作使左操作数容器的所有迭代器失效。swap 操作则不会使迭代器失效。完成 swap 操作后,尽管被交换的元素已经存放在另一容器中,但迭代器仍然指向相同的元素。
由于 assign 操作首先删除容器中原来存储的所有元素,因此,传递给 assign 函数的迭代器不能指向调用该函数的容器内的元素。
带有一对迭代器参数的 assign 操作允许我们将一个容器的元素赋给另一个不同类型的容器。
关于 swap 的一个重要问题在于:该操作不会删除或插入任何
元素,而且保证在常量时间内实现交换。由于容器内没有移动
任何元素,因此迭代器不会失效。
4 vector容器的自增长
4-1 capacity 和 reserve 成员
vector 类提供了两个成员函数:capacity 和 reserve 使程序员可与 vector 容器内存分配的实现部分交互工作。capacity 操作获取在容器需要分配更多的存储空间之前能够存储的元素总数,而 reserve 操作则告诉 vector 容器应该预留多少个元素的存储空间。
size 和 capacity 的区别:
size 指容器当前拥有的元素个数;
capacity 则指容器在必须分配新存储空间之前可以存储的元素总数。
5 容器的选用
通常来说,除非找到选择使用其他容器的更好理由,否则 vector 容器都是最佳选择。
6 容器适配器
头文件
#include <stack>
#include <queue>
默认的 stack 和 queue 都基于 deque 容器实现,而 priority_queue 则在 vector 容器上实现。在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型实参,可覆盖其关联的基础容器类型:
// empty stack implemented on top of vector
stack< string, vector<string> > str_stk;
// str_stk2 is implemented on top of vector and holds a copy of svec
stack<string, vector<string> > str_stk2(svec);
对于给定的适配器,其关联的容器必须满足一定的约束条件。stack 适配器所关联的基础容器可以是任意一种顺序容器类型。
因此,stack 栈可以建立在 vector、list 或者 deque 容器之上。
而 queue 适配器要求其关联的基础容器必须提供 push_front 运算,因此只能建立在 list 容器上,而不能建立在 vector 容器上。
priority_queue 适配器要求提供随机访问功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。
6-1 栈适配器
实例
// number of elements we'll put in our stack
const stack<int>::size_type stk_size = 10;
stack<int> intStack; // empty stack
// fill up the stack
int ix = 0;
while (intStack.size() != stk_size)
// use postfix increment; want to push old value onto intStack
intStack.push(ix++); // intStack holds 0...9 inclusive
int error_cnt = 0;
// look at each value and pop it off the stack
while (intStack.empty() == false) {
int value = intStack.top();
// read the top element of the stack
if (value != --ix) {
cerr << "oops! expected " << ix
<< " received " << value << endl;
++error_cnt;
}
intStack.pop(); // pop the top element, and repeat
}
cout << "Our program ran with "
<< error_cnt << " errors!" << endl;
声明语句:
stack<int> intStack; // empty stack