顺序容器(sequential container):容器内的元素根据位置来存储和访问,元素的排列次序与元素值无关。
(1)标准库定义了三种顺序容器类型:vector、list、deque
(2)三种容器的差别在于:访问元素的方式,以及添加或删除元素相关操作的运行代价
容器只定义了少量操作,大多数额外操作由算法库提供;所有容器都是类模板。
一、容器元素的初始化
C<T> c; 创建名为c的空容器
C<T> c(c2); 创建容器c2的副本c
C<T> c(b, e); 创建容器c,其元素是迭代器 b 和 e 标识范围内的元素的副本(使用形参为一对迭代器的constructor)
C<T> c(n, t); 创建容器c,其元素为 n 个 T 类型的 t 值 【只适用于顺序容器】
C<T> c(n); 创建有 n 个 T 类型元素的容器c,其元素值通过默认构造函数给出 【只适用于顺序容器】
例:sequential container
list<int> ilist; //假设ilist的元素为2,3,6,9,13
while (cin >> t)
{
if (t=='Q') break;
ilist.push_back(t);
ilist.push_front(t-1); //适用于list和deque容器类型
}
// insert操作,新元素插入在迭代器指向位置『之前』
ilist.insert(ilist.begin(), -13);
ilist.insert(ilist.end(), 13);
// 下标操作(list容器不支持),以下语句错误
// for(list<int>::size_type ix = 0; ix != ilist.size(); ++ix)
// cout << ilist[ix] << ',';
// 迭代器
for(list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
cout << *iter << ',';
cout << endl;
list<int> ilist_copy(ilist); // ilist_copy的元素也为2,3,6,9,13
list<int> ilist_copy1(ilist.begin(),ilist.end()); // ilist_copy1的元素也为2,3,6,9,13
list<string> slist1(8, "hi"); // slist1的元素为hi,hi,hi,hi,hi,hi,hi,hi
list<string> slist2(9); // slist2的元素为,,,,,,,(每个元素都是空字符串)
list<int> ilist1(8,1); // ilist1的元素为1,1,1,1,1,1,1,1
list<int> ilist2(9); // ilist2的元素为0,0,0,0,0,0,0,0,0(每个元素的默认值为0)
// 不能直接将一种容器内的元素复制给另一种元素,但是可以通过传递一对迭代器间接实现该功能。
vector<string> svec(3,"hi"); // svec的元素为hi,hi,hi,he,he,he
svec.push_back("he");
svec.push_back("he");
svec.push_back("he");
list<string> slist3(svec.begin(),svec.end()); // slist3的元素也为hi,hi,hi,he,he,he
vector<string>::iterator mid = svec.begin() + svec.size()/2;
deque<string> front(svec.begin(),mid); // front的元素为hi,hi,hi
deque<string> back(mid,svec.end()); // back的元素为he,he,he
二、容器内元素的类型约束
【最低要求】必须满足以下两个条件:
(1)元素类型必须支持赋值运算
(2)元素类型的对象必须可以复制
除了引用类型之外(引用不支持赋值),所有内置或者复合类型都可以用作元素类型。
除了IO库以及auto_ptr类型,所有其他标准库类型都是有效的容器元素类型。
容器的容器:容器本身也满足上述要求,可定义元素是容器类型的容器。
vector< vector<string> > lines; // vector of vector,必须在<和v(>和>)之间使用空格
例:containers of container
vector<string> svec0(3, "hi");
vector<string> svec1(3, "he");
vector<string> svec2(3, "wd");
vector< vector<string> > lines;
lines.push_back(svec0);
lines.push_back(svec1);
lines.push_back(svec2);
for(vector< vector<string> >::iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
for(vector<string>::iterator iter1 = iter->begin(); iter1 != iter->end(); ++iter1)
cout << *iter1 << ',';
cout << endl;
}
cout << endl;
其结果为:
hi,hi,hi,
he,he,he,
wd,wd,wd,
三、迭代器 iterator
使用a pair of iterator(一对迭代器)来标记iterator range(迭代器范围),通常分别指向同一个容器中的两个元素或超出末端的下一位置,用于标记容器中的一段元素范围。
常用迭代器运算:
*iter // 返回迭代器iter所指向元素的引用(iter相当于指针)
iter->mem // 获取指定元素中名为mem的成员,等效于(*iter).mem
++iter 和 iter++ // iter值自加,使其指向容器里的下一个元素
--iter 和 iter-- // iter值自减
iter1 == iter2 和 iter1 != iter2 // 比较两个迭代器是否相等(或不等)
vector和deque容器的迭代器还支持(list容器既不支持算术运算,也不支持关系运算):
iter + n 和 iter - n
iter += iter2 和 iter -= iter2
iter1 - iter2
> >= < <= // 关系运算
特别地,一些容器操作会修改容器的内在状态或移动容器内的元素,这样的操作会使得所有指向被移动元素的迭代器失效,也可能同时使其他迭代器失效。
例如,使用erase函数删除了容器中某个元素,指向这个被删除元素的迭代器就无效了。
四、顺序容器的操作
1. 容器定义的类型:
size_type // 无符号整型
iterator和const_iterator // 迭代器类型,只读迭代器类型
reverse_iterator和const_reverse_iterator // 逆序迭代器类型,只读逆序迭代器类型
difference_type // 存储两个迭代器差值的有符号整型(可为负数)
value_type // 元素类型
reference和const_reference // 元素的左值类型(value_type&),元素的常量左值类型(const value_type&)
其中,最后三种类型(value_type, reference, const_reference),无需直接知道容器元素的真正类型就能使用。
list<string>::iterator iter;
vector<int>::difference_type cnt;
vector<string>::value_type str;
2. 添加元素相关操作
在容器中添加元素时,系统是将元素值复制到新容器里,新容器存放的是原始元素的副本。
被复制的原始值与新容器中的元素各不相干,即:容器内元素值发生变化时,被复制的原始值不会受到影响。
list<int> ilist;
ilist.begin(); // 返回指向容器的第一个元素的迭代器
ilist.end(); // 返回指向容器的最后一个元素的下一个位置的迭代器
ilist.push_back(t); // 在容器的尾部,添加值为 t 的元素,返回void类型 【例如, ilist.push_back(2); 此时,ilist为1 】
ilist.push_front(t); // 在容器的前端,添加值为 t 的元素, 返回void类型 【例如, ilist.push_front(1); 此时,ilist为1,2 】
ilist.insert(p, t); // 在迭代器 p 所指向元素的前面,添加值为 t 的元素, 【例如, ilist.insert(ilist.end(), 3); 此时,ilist为1,2,3 】
// 返回指向新添加元素的迭代器
ilist.insert(p, n, t); // 在迭代器 p 所指向元素的前面,添加 n 个值为 t 的元素,返回void类型
ilist.insert(p, b, e); // 在迭代器 p 所指向元素的前面,插入由迭代器 b 和 e 标记范围内的元素,返回void类型
例:
list<int> ilist;
list<int>::value_type a=11;
ilist.push_back(2);
ilist.push_front(1);
list<int>::iterator iliter = ilist.insert(ilist.end(), 3); // iliter指向第3个元素(即:第3个位置)
ilist.insert(ilist.end(), a);
ilist.insert(ilist.end(), 3, 8);
for(list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
cout << *iter << ',';
cout << endl;
list<int> ilist_copy(ilist.begin(), iliter); // 将第3个位置作为end,所以ilist_copy只包含前ilist的2个元素
for(list<int>::iterator iter = ilist_copy.begin(); iter != ilist_copy.end(); ++iter)
cout << *iter << ',';
cout << endl;
string str[6] = {"stra", "strb", "strc", "strd", "stre", "strf"};
list<string> slist(6, "hi");
for(list<string>::iterator iter = slist.begin(); iter != slist.end(); ++iter)
cout << *iter << ',';
cout << endl;
slist.insert(slist.end(), str, str+2); // 插入数组中的一段元素(str+2表示end,也就是将str+1作为最后一个元素)
for(list<string>::iterator iter = slist.begin(); iter != slist.end(); ++iter)
cout << *iter << ',';
cout << endl;
slist.insert(slist.end(), str+3, str+5); // str+3是插入的第一个元素,str+5表示end,也就是str+4是插入的最后一个元素
for(list<string>::iterator iter = slist.begin(); iter != slist.end(); ++iter)
cout << *iter << ',';
cout << endl;
结果为:
1,2,3,11,8,8,8,
1,2,
hi,hi,hi,hi,hi,hi,
hi,hi,hi,hi,hi,hi,stra,strb,
hi,hi,hi,hi,hi,hi,stra,strb,strd,stre,
3. 其他操作
(1)容器大小操作
ilist.size(); // 返回容器的元素个数,返回类型为list<int>::size_type
ilist.max_size(); // 返回容器可容纳的最大元素个数
ilist.empty(); // 返回判断容器大小是否为0的布尔值
ilist.resize(n); // 重新调整容器的长度,使其可以容纳 n 个元素
ilist.resize(n, t); // 重新调整容器的长度,使其可以容纳 n 个元素,且所有新添加的元素值都为 t 值
例:
list<int> ilist(10,1);
for(list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
cout << *iter << ',';
cout << endl;
ilist.resize(12); // 空出来位置的元素值为 默认构造函数提供的初始值
cout << ilist.size() << endl;
for(list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
cout << *iter << ',';
cout << endl;
ilist.resize(15, -1); // 增加了3个位置的容量,且增加的3个位置的值设置为-1
for(list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
cout << *iter << ',';
cout << endl;
ilist.resize(5); // 减少了容器的容量,只保留能够容纳的那部分元素,去掉多余的元素
for(list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
cout << *iter << ',';
cout << endl;
结果为:
1,1,1,1,1,1,1,1,1,1,
12
1,1,1,1,1,1,1,1,1,1,0,0,
1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,
1,1,1,1,1,
(2)访问元素
如果容器非空,容器类型的front和back成员将返回容器内的第一个和最后一个元素的引用。
ilist.back(); // 返回容器最后一个元素的引用
ilist.front(); // 返回容器第一个元素的引用
ilist[n]; // 返回下标为 n 的元素的引用【仅适用于vector和deque容器】
ilist.at(n); // 返回下标为 n 的元素的引用【仅适用于vector和deque容器】
例:
vector<int> ivec;
for(int i = 1; i<10; i++)
ivec.push_back(i);
for(vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
cout << *iter << ',';
cout << endl;
for(vector<int>::size_type n = 0; n != ivec.size(); ++n)
cout << ivec[n] << ',';
cout << endl;
for(vector<int>::size_type n = 0; n != ivec.size(); ++n)
cout << ivec.at(n) << ',';
cout << endl;
if (!ivec.empty())
{
list<int>::reference val = *ivec.begin();
list<int>::reference val2 = ivec.front();
cout << "the first element of ivec: " << val << ", " << val2 << endl;
list<int>::reference last = *--ivec.end();
list<int>::reference last2 = ivec.back();
cout << "the last element of ivec: " << last << ", " << last2 << endl;
}
cout << endl;
输出结果:
1,2,3,4,5,6,7,8,9, // 迭代器dereference
1,2,3,4,5,6,7,8,9, // ivec[n]
1,2,3,4,5,6,7,8,9, // ivec.at(n)
the first element of ivec: 1, 1
the last element of ivec: 9, 9