C++顺序容器
1. 顺序容器的定义和初始化
顺序容器是将单一类型元素聚集起来,方便进行管理。标准库定义三种顺序容器:
- vector 支持快速随机访问
- list 支持快速插入删除
- deque 双端队列
他们差别在于访问元素的方式以及添加或删除元素相关操作的运行代价。标准库还提供了三种容器适配器:
- stack 后进先出LIFO 栈
- queue 先进先出FIFO 队列
- priority_queue 优先队列
容器仅定义少量操作,大多数由算法库提供额外的操作。
顺序容器的定义要包括相关头文件和命名空间std
<vector> <list> <deque>
三个头文件
容器初始化四种方式
调用容器的默认构造函数初始化
这些将会调用容器的默认构造函数去创造空容器对象。
vector<int> ivec;
list<string> slist;
deque<int> ideque;
将一个容器初始化为另一个容器副本
将一个容器复制给另外一个容器时,容器类型必须匹配;容器中元素类型必须相同
vector<int> ivec;
ivec.push_back(1);
ivec.push_back(2);
ivec.push_back(3);
vector<int> icopyVec(ivec);//复制vector的每个元素
//list<int> intList(ivec);//error Notice:将一个容器复制给另一个容器时,容器类型必须匹配,容器中元素类型必须相同
printIntVector(icopyVec);
ivec.clear();
printIntVector(icopyVec);
vector<string> stringvec;
stringvec.push_back("hello");//复制C风格字符串到内存中保存
stringvec.push_back("world");
stringvec.push_back("good");
vector<string> stringcopyVec(stringvec); //所有字符串元素都复制一遍
printStringVector(stringcopyVec);
stringvec.clear();
printStringVector(stringcopyVec);
初始化为一段元素的副本
传递一对迭代器将一种容器内元素复制给另一个容器,不要求容器类型相同,只要求容器元素能够兼容即可实现复制。
//initialize slist with copy of each element of svec
list<string> slits(svec.begin(),svec.end());
//find midpoint in the vector
vector<string>::iterator mid=svec.begin()+svec.size()>>1;
//initialize front with first half of svec:the elements up to but not including *mid
deque<string> front(svec.begin(),mid);
//initialize fronback with second half of svec:the elements *mid through end of svec
deque<string> back(mid,svec.end());
我们知道指针就是迭代器,通过使用内置数组中的一对指针初始化容器
char *words[]={"china","tea","chinese"};
cout<<*(words+1)<<endl; //tea
cout<<words[1]<<endl; //tea
size_t words_size=sizeof(words)/sizeof(char *);
cout<<"word_size="<<words_size<<endl; //3
words_size=sizeof(words)/sizeof(words[0]);
cout<<"word_size="<<words_size<<endl; //3
list<string> stringlist2(words,words+words_size);
printStringList(stringlist2);
string s[]={"china","tea","chinese"};
words_size=sizeof(s)/sizeof(s[0]);
cout<<"word_size="<<words_size<<endl; //3
words_size=sizeof(s)/sizeof(string);
cout<<"word_size="<<words_size<<endl; //3
分配和初始化指定数目的元素
可以显示指定容器大小和(可选)元素初始化式。容器大小可以是常量或非常量表达书,元素初始化式必须是可用于初始化其容器元素类型的对象的值。不提供元素初始化式,采用值初始化方式,对内置类型,全部初始化为0,对类对象,调用默认构造函数初始化
const list<int>::size_type list_size=10;
list<string> slist(list_size,"english");
extern unsigned int get_words_count(const string &);
vector<string> svec(get_words_count("china"));
2. 复制容器对象的构造函数和使用两个迭代器的构造函数
区别
复制容器对象的构造函数要求 两个容器类型相同,元素类型也相同
使用两个迭代器的构造函数仅仅要求容器元素类型能够相互兼容,不要求容器类型必须相同,此外,两个迭代器还可以传递数组类型指针。
3. 容器内元素类型约束
容器元素类型必须满足
- 元素类型必须支持赋值运算(即必须支持=重载运算符)
- 元素类型的对象必须可复制(即具有拷贝构造函数)
除了引用类型外,所有内置和复合类型都满足。除IO库之外,所有其他标准库类型都可做容器的元素类型。特别的,容器本身也满足。可以定义容器的容器
vector< vector<string> > lines;
4. 迭代器和迭代器范围
每种容器都提供若干共同工作的迭代器类型。所有迭代器都具有相同接口。迭代器为所有标准库容器类型提供的运算
迭代器运算 | 说明 |
*iter | 返回迭代器iter所指向元素的引用 value_type & |
iter->mem | 对iter解引用,获取指定元素对象的mem成员 (*iter).mem |
++iter | iter+1使其指向下一个元素 |
iter++ | |
--iter | iter-1使其指向前一个元素 |
iter-- | |
iter1!=iter2 | 比较两个迭代器是否相等,只有当两个迭代器指向同一容器的同一元素或都指向容器元素末尾的下一位置才相等 |
iter1==iter2 |
由于vector和deque底层实现是基于内置数组的,因此其迭代器还支持算术运算和其他关系操作符
运算 | 说明 |
---|---|
iter +/- n | 在迭代器上加减n,产生指向当前迭代器后面(前面)第n元素,新计算出来的迭代器必须指向容器元素或容器末尾下一位置 |
iter1+/-iter2 | 运算所得结果类型为difference_type |
iter +=/-= n | n必须自我保证合法 |
< <= > >= | 关系操作符都支持 |
迭代器范围
迭代器范围分别指向同一容器中的两个元素或超出末端的下一位置。左闭合区间 [first,last) 或者 [beg,end)
有效迭代器:
- first和last必须指向同一容器中的元素或超出容器末端的下一位置
- first经过有限次++必能到达last,即first位于last之前
编译器不能自己保证上述要求。如果不能满足上述条件,运行时将发生未知定义。
使用左闭合区间意义
- 当first==last时,迭代器范围为空,标识容器为空
- 当first!=last时,迭代器内至少有一个元素
exercise
编写函数,形参是一对迭代器和int型数值,实现在迭代器范围内查找给定元素,找到返回元素所在迭代器位置,找不到返回容器末尾下一位置
//函数定义
template<class _Init,class _Ty>
_Init MyFind(_Init first,_Init last,const _Ty &value)
{
for (;first!=last;++first)
{
if (*first==value)
{
break;
}
}
return first;
}
//函数调用
#define ArrayLength(X) ((sizeof(X))/(sizeof(X[0])))
int array[]={1,2,3,4,5,6,7};
vector<int> ivec(array,array+ArrayLength(array));
if(MyFind<vector<int>::iterator,int>(ivec.begin(),ivec.end(),7)!=ivec.end())
{
cout<<"find element of 7"<<endl;
}
使迭代器失效的操作
一些容器操作将会修改容器的内在状态或移动容器内的元素,使得迭代器失效,在编程时,必须小心使用。
常用几个小例子避免失效迭代器
- 循环for遍历容器
//ivec.end()将会每次都返回容器末尾下一位置,最好不要存储此变量以防容器失效
for(vector<int>::iterator beg=ivec.begin();beg!=ivec.end();++beg){//...}
- 与容器大小有关计算
//ivec.size()返回容器大小,可能会变动
vector<int>::iterator mid=ivec.begin()+ivec.size()>>1;