(写于August 2nd, 2013)
很久没有看c++了,突然想起之前写的(上)简单的介绍了c++中最常用的顺序容器:vector类型,一直没有写(下),现在我决定对(上)的内容进行扩充与完善。
之前介绍过标准库vector类型,是一种顺序容器。它将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这写元素。顺序容器的元素排列次序与元素值无关,而是有元素添加到容器里的次序决定。
容器元素的初始化
1.分配制定数目的元素,并对这写元素进行值初始化:
vector<int> ivec(10); //ivec包含10个0值元素
接受容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这初始化。
2.分配制定数目的元素,并将这些元素初始化为定值:
vector<int> ivec(10,1);//ivec包含10个值为1个元素
3.将vector对象初始化为一段元素的副本:
int ia[10]={0,1,2,3,4,5,6,7,8,9};//指针就是迭代器,因此可以使用内置数组中的一对指针初始 //化容器
vector<int> ivec(ia,ia+10);//ivec包含10个元素,值分别为0~9
4.将一个vector对象初始化为另一vector对象的副本:
vector<int> ivec1(10,1)
vector<int> ivec2(ivec1);
//ivec2包含10个值为1的元素(与ivec1相同)
复制容器对象的构造函数与使用两个迭代器的构造函数之间的差别:
复制容器对象的构造函数只能将一个容器初始化为另一个容器的副本(即复制另一个容器的全部元素),这种构造函数要求两个容器是同类型的;使用两个迭代器的构造函数可以将一个容器初始化为另一个容器的子序列(即复制另一个容器的一个子序列),而且采用这种构造函数不要求两个容器是同类型的。
容器内元素的类型约束
容器元素类型必须满足以下两个约束:
元素类型必须支持赋值运算。
元素类型必须可以复制。
(引用类型不支持一般意义上的赋值运算,因此没有元素是引用类型的容器。
也可以定义元素本身就是容器类型的容器。)
没有提供默认构造函数的类:
(假设类Foo没有默认构造函数,但提供了需要一个int型形参的构造函数)
vector<Foo> empty;//定义了一个存放Foo类型对象的空容器
vector<Foo> bad(10);//error:类Foo没有提供默认构造函数
vector<Foo> ok(10,1);//定义了一个存放10个1(int型)的容器
只有在同时指定每个元素的初始化式时,才能使用给定容器大小的构造函数来创建同类型的容器对象。
定义容器的容器:
vector< vector<string> > lines;
(注意:必须用空格隔开两个相邻的>符号,否则系统会末认为是>>是右移操作)
迭代器和迭代器范围
所有的迭代器具有相同的接口:如果某中迭代器支持某种操作,那么支持这种操作的其他迭代器也会以相同的方式支持这种操作。
常用迭代器运算:
*iter返回迭代器iter所指向的元素的引用
iter->mem对iter进行解引用,获取制定元素中名为men的成员。等效于(*iter).mem
++iter(iter++)给iter加1,使其指向容器里的下一个元素
--iter(iter--)给iter减1,使其指向容器里的前一个元素
iter1==iter2比较两个迭代器是否相等(或不等)。当两个迭代器指向同iter1!=iter2一个容器中的同一个元素,或者当他们都指向同一个容器的超出末端的下一位置时,两个迭代器相等
(以下是vector和deque容器的迭代器提供额外的运算)
iter+n在迭代器上加(减)整数值n,将产生指向容器中前面(后iter-n面)第n个元素的迭代器。新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一位置
iter+=iter2这是迭代器加减法的复合赋值运算:将iter加上或减去iter1-=iter2iter2的运算结果赋给iter1
iter1-iter2两个迭代器的减法,其运算结果加上右边的迭代器即得左边的迭代器。这两个迭代器必须指向同一个容器中的元素或超出容器中末端的下一位置
>,>=,<,<=迭代器的关系操作符。当一个迭代器指向的元素在容器中位于另一个迭代器指向的元容器中的元素或超出容器末端的下一位置
(以下是vector容器逆序输出所存的数字)
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
vector<int> vector1(a, a + 9);
vector<int>::iterator iter1 = vector1.begin(),
iter2 = vector1.end();
while (iter1 < iter2)
cout<<*(--iter2);
cout<<endl;
return 0;
}
c++语言使用一对迭代器标记迭代器范围,这个两个迭代器分别指向同一个容器中的两个元素或超出末端的下一位置。
迭代器first与last如果满足以下条件,则可形成一个迭代器范围:
它们指向同一个容器中的元素或超出末端的下一位置。
如果这两个迭代器不相等,则对first反复做自增运算必须能够到达last。换句话说,在容器中,last绝对不能位于first之前。
(以下是vector查找元素)
#include <iostream>
#include <vector>
using namespace std;
vector<int>::iterator findInt(vector<int>::iterator beg, vector<int>::iterator end, int ival);
int main()
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}, b = 7;
vector<int> ivec(a, a + 9);
vector<int>::iterator it;
it=findInt(ivec.begin(), ivec.end(), b);
if(it != ivec.end())
cout<<"找到"<<endl;
else
cout<<"未找到"<<endl;
return 0;
}
vector<int>::iterator findInt(vector<int>::iterator beg, vector<int>::iterator end, int ival)
{
while(beg != end)
{
if(*beg == ival)
break;
else
++beg;
}
return beg;
}
顺序容器的操作
1在容器中添加元素。
2在容器中删除元素。
3设置容器大小。
4(如果有的话)获取容器内的第一个和最后一个元素。
容器定义的类型别名:
size_type 无符号整型,足以存储此容器的最大可能容器长度
iterator 此容器类型的迭代器类型
const_iterator 元素的只读迭代器类型
reverse_iterator 按逆序寻址元素的迭代器
const_reverse_iterator 元素的只读(不能写)逆序迭代器
difference_type 足够存储两个迭代器差值的有符号整形,可为负数
value_type 元素类型
reference 元素的左值类型,是value_type&的同义词
const_reference 元素的常量左值类型,等效于const value_type&
在容器中利用insert函数插入元素的三个版本:
1.需要一个迭代器和一个元素值参数迭代器指向插入新元素的位置,insert函数在迭代器指向位置之前插入元素。函数返回指向新插入元素的位置的迭代器。
2.insert提供在指定位置插入指定数量的相同元素的功能。
3.insert可实现在容器中插入由一对迭代器标记的一段范围内的元素。
注意:添加元素可能会使迭代器失效。在vector中添加元素可能会导致整个容器的重新加载,该容器涉及的所有迭代器都会失效。因此当编写循环将元素插入到容器中,程序必须确保迭代器在每次循环后都得到更新。
(以下是:将int型的list容器的所有元素复制在两个vector容器中,如果是偶数复制到一个vector中,奇数复制到另一个vector中)
#include <iostream>
#include <vector>
#include <list>
using namespace std;
int main(int argc, char *argv[])
{
list<int> ilist;
vector<int> svec1, svec2;
int ival;
vector<int>::iterator iter1 = svec1.begin(), iter2 = svec2.begin();
/*读入int对象并存储在ilist对象中*/
cout<<"请输入整型(-1结束输入):"<<endl;
while (cin>>ival && ival != -1)
{
ilist.push_back(ival);
}
/*复制ilist对象的元素至适当的vector对象*/
for (list<int>::iterator iter = ilist.begin(); iter != ilist.end(); ++iter)
{
if (*iter%2 == 0)
{
iter1 = svec1.insert(iter1, *iter);
iter1++;
}
else
{
iter2 = svec2.insert(iter2, *iter);
iter2++;
}
}
/*检验输出*/
vector<int>::iterator it1 = svec1.begin(), it2 = svec2.begin();
cout<<"偶数vector:"<<endl;
while (it1 != svec1.end())
{
cout<<*it1<<"\t";
it1++;
}
cout<<endl;
cout<<"奇数vector:"<<endl;
while (it2 != svec2.end())
{
cout<<*it2<<"\t";
it2++;
}
cout<<endl;
return 0;
}
关于删除元素:erase,pop_front,pop_back函数使指向被删除元素的所有迭代器失效。对于vector容器,指向删除点后面的元素迭代器通常也会失效。
(以下是:删除vector中偶数值以及list中奇数值)
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
int ia[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89};
list<int> ilist(ia, ia + 11);
vector<int> svec(ia, ia + 11);
list<int>::iterator iter1 = ilist.begin();
vector<int>::iterator iter2 = svec.begin();
while (iter1 != ilist.end())
{
if(*iter1 % 2 != 0)
{
iter1 = ilist.erase(iter1);
}
else
{
iter1++;
}
}
while (iter2 != svec.end())
{
if(*iter2 % 2 == 0)
{
iter2 = svec.erase(iter2);
}
else
{
iter2++;
}
}
iter1 = ilist.begin();
iter2 = svec.begin();
cout<<"ilist:"<<endl;
while (iter1 != ilist.end())
{
cout<<*iter1<<"\t";
iter1++;
}
cout<<"\nvector:"<<endl;
while (iter2 != svec.end())
{
cout<<*iter2<<"\t";
iter2++;
}
cout<<endl;
return 0;
}
顺序容器的其他操作函数:
1.size():返回容器的元素个数
2.empty():返回标记容器大小是否为0的布尔值
3.resize(n)/resize(n, t):调整容器大小,使其能容纳n个元素;或调整容器大小,使其能容纳n个元素,所有新添加的元素都为t
4.back():返回容器的最后一个元素的引用
5.front():返回容器第一个元素的引用
6.(仅适用vector和deque)c[n]或at(n)返回下标为n的元素的引用
7.c1.swap(c2):交换内容,c1存放c2元素,c2存放c1元素,交换后迭代器也随之交换并不会失效
8.assign(b, e):重新设置容器元素,将的迭代器b和e标记的范围内所有的元素复制到容器中
9.assign(n, t):将容器重新设置为存储n个t
反向迭代器
反向迭代器是一种反向遍历容器的迭代器。也就是,从最后一个元素到第一个元素遍历容器。反向迭代器将自赠(和自减)的含义反了过来:对于反向迭代器,++运算将访问前一个元素,而--运算则访问下一个元素。正如容器不仅定义了begin与end用来指向容器首元素和尾元素下一位置的迭代器,还定义了rbegin和rend成员,分别返回指向容器尾元素和首元素前一位置的反向迭代器。
反向迭代器用于表示范围,而所表示的范围是不对称的,因此使用普通迭代器对反向迭代器复制或初始化时,所得到的迭代器并不是指向原迭代器所指向的元素。