容器的使用
容器和适配器都是针对于泛型数据而设计的,所以在使用过程中都需要 ”动态地绑定“ 数据类型。STL中各种容器适配器的使用基本相同,不同之处也正好体现出它们的特色,比如:队列的 ”先进先出“ 、向量的随机访问等等。如下面的简单操作:
#include<iostream>
#include<vector>
#include<deque>
#include<list>
using namespace std;
void main()
{
vector<int> v;
deque<float> d;
list<double> l;
v.push_back(1);
v.push_back(2);
d.push_back(3.4f);
d.push_front(2.3f);
l.push_back(5.1);
l.push_front(3.4);
cout<<v.front()<<" "<<v[1]<<endl;
cout<<d.front()<<" "<<d[1]<<endl;
cout<<l.front()<<endl;
}
1、vector有push_back 和 pop_back 操作,表明该容器的特点是:插入和删除操作只能在尾部实现,可以直接通过下标实现 随机 访问;
2、deque的插入和删除可以在头部或者尾部,表明它是双向的队列,也可以通过下标进行随机访问;
3、list的插入和删除可以在头部或者尾部,但是无法通过下标实现随机访问(只能通过遍历的方式实现顺序访问),体现出链式结构的特点。
迭代器
先看一下通过指针遍历数组的代码:
#include<iostream>
using namespace std;
void main()
{
int x[]={1,2,3,4,5,6,7,8,9};
int *p;
for(p=x;p<x+sizeof(x)/sizeof(int);p++)
cout<<*p<<" ";
cout<<endl;
}
很显然,容器这种 ”特殊“ 的数组,也非常需要这样的统一操作。难点在于,有些容器并非像数组一样 顺序 存储数据。STL提供迭代器(iterator)来实现这样的功能。
#include<iostream>
#include<vector>
using namespace std;
void main()
{
int x[]={1,2,3,4,5,6,7,8,9};
vector<int> v(x,x+9);
vector<int>::iterator it;
for(it=v.begin();it!=v.end();it++)
cout<<*it<<" ";
cout<<endl;
}
1、iterator 是一个类似指针的迭代器,可以遍历容器,所以iterator的定义必须与需要遍历的容器类型进行绑定;
2、容器和适配器都提供了begin 和 end 的函数,供 iterator 遍历;
3、重载了++ 和 * 等运算符,统一了迭代器的操作;
4、能够随机访问的容器,具有下标 和 迭代器 遍历的两种方式。(类似数组可以通过下标或者指针来遍历)
5、迭代器也可以分为:可读、可写、前向、后向、双向、随机等类型,在某些应用场景中可以使用特定类型的迭代器可以提高代码的效率和安全性。
容器的通用操作
1、初始化--每个容器都提供了一个默认构造函数,一个拷贝构造函数,如:
vector<int> v(ls.begin(),ls.end()); 以某容器 ls 进行初始化 (ls可以是vector、list、deque等等)
set<int> s(a,a+sizeof(a)/sizeof(int)); 也可以是数组 int a[];
2、与大小相关的操作函数:
c.size() 返回元素个数 c.empty() 判断是否为空,速度比判断元素个数为0快
3、迭代器函数:
begin() 第一个元素 rbegin():逆向的第一个元素
end() 最后一个元素的后面一个元素 rend() : 逆向的最后一个元素的后面一个元素
4、比较运算 == != < <= > >=
5、插入元素函数(如果有的话) insert push_front push_back
6、删除元素函数(如果有的话) erase pop_back pop_front
7、清除所有元素函数 clear