C++的STL中,有哪几种容器?
在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器。
vector是一段连续的内存地址,基于数组实现,其提供了自动内存管理功能(采用了STL普遍的内存管理器allocator),可以动态改变对象长度,提供随机访问。在尾部添加和删除元素的时间是常数的,但在头部或中间就是线性时间。
在vector容器中有以下几个关于大小的函数:
vector内存扩展方式:
一般需要注意vector的内存分配问题,原因是:
避免内存重新配置的方法:
std :: vector v; //create an empty vector
v.reverse(80); //reserve memory for 80 elements
std::vector v(80);
//1.定义和初始化
vector vec1; //默认初始化,vec1为空
vector vec2(vec1); //使用vec1初始化vec2
vector vec3(vec1.begin(),vec1.end()); //使用vec1初始化vec2
vector vec4(10); //10个值为0的元素
vector vec5(10,4); //10个值为4的元素
//2.常用操作方法
vec1.push_back(100); //尾部添加元素
int size = vec1.size(); //元素个数
bool isEmpty = vec1.empty(); //判断是否为空
cout<=、<=
vector :: iterator iter = vec1.begin(); //获取迭代器首地址
vector :: const_iterator c_iter = vec1.begin(); //获取const类型迭代器
vec1.clear(); //清空元素
//3.遍历
//下标法
int length = vec1.size();
for(int i=0; i :: iterator iter = vec1.begin();
for( ; iter != vec1.end(); iter++)
{
cout<<*iter;
}
list是非连续的内存,基于链表实现,属于循环双向链表,目的是实现快速插入和删除,但是随即访问却是比较慢。
//1.定义和初始化
list lst1; //创建空list
list lst2(3); //创建含有三个元素的list
list lst3(3,2); //创建含有三个元素为2的list
list lst4(lst2); //使用lst2初始化lst4
list lst5(lst2.begin(), lst2.end()); //同lst4
//2.常用操作方法
lst1.assign(lst2.begin(),lst2.end()); //分配3个值为0的元素
lst1.push_back(10); //末尾添加值
lst1.pop_back(); //删除末尾值
lst1.begin(); //返回首值的迭代器
lst1.end(); //返回尾值的迭代器
lst1.clear(); //清空值
bool isEmpty1 = lst1.empty(); //判断为空
lst1.erase(lst1.begin(),lst1.end()); //删除元素
lst1.front(); //返回第一个元素的引用
lst1.back(); //返回最后一个元素的引用
lst1.insert(lst1.begin(), 3, 2); //从指定位置插入个3个值为2的元素
lst1.rbegin(); //返回第一个元素的前向指针
lst1.remove(2); //相同的元素全部删除
lst1.reverse(); //反转
lst1.size(); //含有元素个数
lst1.sort(); //排序
lst1.unique(); //删除相邻重复元素
//3.遍历
//迭代器法
for(list :: const_iterator iter = lst1.begin(); iter != lst1.end(); iter++)
{
cout<<*iter;
}
双端队列(double-ended queue),支持随机访问,与vector类似,主要区别在于,从deque对象的开始位置插入和删除元素的时间也是常数的,所以若多数操作发生在序列的起始和结尾处,则应考虑使用deque。为实现在deque两端执行插入和删除操作的时间为常数时间这一目的,deque对象的设计比vector更为复杂,因此,尽管二者都提供对元素的随机访问和在序列中部执行线性时间的插入和删除操作,但vector容器执行这些操作时速度更快些。
#include
#include
#include
#include
using namespace std;
int main()
{
//1. 初始化
deque v;
deque :: iterator iv;
deque v1{ 1, 2, 3, 4, 5};
deque v2(v1);
deque v3(v1.begin(), v1.end());
//v.assign(10, 2); //将10个值为2的元素赋到deque中
v.assign(v1.begin(), v1.end()); //接受序列容器的范围
cout << v.size() << endl; //返回deque实际含有的元素数量
//2. 添加
v.push_front(666);
for (int i = 0; i < 10; i++)
v.push_back(i);
for_each(v.begin(), v.end(), print); //需要#include , print为自定义输出函数
cout << v.size() << endl;
//3. 插入及遍历、逆遍历
v.insert(v.begin() + 3, 99);
v.insert(v.end() - 3, 99);
for_each(v.begin(), v.end(), print);
for_each(v.rbegin(), v.rend(), print); //在逆序迭代器上做++运算将指向容器中的前一个元素
//一般遍历写法
for(iv = v.begin(); iv != v.end(); ++iv)
cout << *iv << " ";
//4. 删除
v.erase(v.begin() + 3);
for_each(v.begin(), v.end(), print);
v.insert(v.begin() + 3, 99); //还原
v.erase(v.begin(), v.begin() + 3); //注意删除了3个元素而不是4个
for_each(v.begin(), v.end(), print);
v.pop_front();
v.pop_back();
for_each(v.begin(), v.end(), print);
//5. 查询
cout << v.front() << endl;
cout << v.back() << endl;
//危险的做法,但一般我们就像访问数组那样操作就行
for (int i = 15; i < 25; i++)
cout << "Element " << i << " is " << v[i] << endl;
//安全的做法
try
{
for (int i = 15; i < 25; i++)
cout << "Element " << i << " is " << v.at(i) << endl;
}
catch (out_of_range err) //#include
{
cout << "out_of_range at " << i << endl;
}
//6. 清空
v.clear();
cout << v.size() << endl; //0
for_each(v.begin(), v.end(), print); //已经clear,v.begin()==v.end(),不会有任何结果。
return 0;
}
实现了单链表,不可反转。相比于list,forward_list更简单,更紧凑,但功能也更少。
queue是一个适配器类。queue模板让底层类(默认是deque)展示典型的队列接口。queue模板的限制比deque更多,它不仅不允许随机访问队列元素,甚至不允许遍历队列。与队列相同,只能将元素添加到队尾、从队首删除元素、查看队首和队尾的值、检查元素数目和测试队列是否为空。
priority_queue是另一个适配器类,支持的操作与queue相同。两者之间的主要区别在于,在priority_queue中,最大的元素被移到队首。内部区别在于,默认的底层类是vector。可以修改用于确定哪个元素放到队首的比较方式,方法是提供一个可选的构造函数参数:
priority_queue pq1; // default version
priority_queue pg2(greater); // use greater to order, greater<>函数是一个预定义的函数对象。
与queue相似,stack也是一个适配器类,它给底层类(默认情况下为vector)提供了典型的栈接口。
联容器主要有map和set。map是key-value形式的,set是单值。map和set只能存放唯一的key值,multimap和multiset可以存放多个相同的key值。底层都基于树型结构.
map容器提供一个键值对(key-value)容器,map与multimap差别仅仅在于multimap允许一个键对应多个值。对于迭代器来说,可以修改实值,而不能修改key。map会根据key自动排序。
//map有6个构造函数,涉及到内存分配,省略
//1.定义和初始化
map map1; //空map
//2.常用操作方法
map1[3] = "Saniya"; //添加元素
map1.insert(pair(1,"Siqinsini")); //pair方式插入元素
map1.insert(map :: value_type(2, "Diyabi")); //value_type方式入元素
map1.insert(make_pair(4, "V5") ); //make_pair插入元素
string str = map1[3]; //数值方式插入元素,根据key取得value,key不能修改
map :: iterator iter_map = map1.begin();//取得迭代器首地址
int key = iter_map->first; //取得key
string value = iter_map->second; //取得value
map1.erase(iter_map); //删除迭代器数据
map1.erase(3); //根据key删除value
map1.size(); //元素个数
map1.empty(); //判断空
map1.clear(); //清空所有元素
//3.遍历
//正常遍历
for(map :: iterator iter = map1.begin(); iter != map1.end(); iter++)
{
int k = iter->first;
string v = iter->second;
cout<< k << " " << v < :: iterator iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++)
cout << iter->first << " "<< iter->second << endl;
//数组方式遍历,注意此处不是for(int nindex = 0; nindex < nSize; nindex++)
for(int nindex = 1; nindex <= nSize; nindex++)
cout<
set的含义是集合,它是一个有序的容器,里面的元素都是排序好的支持插入、删除、查找等操作,就像一个集合一样,所有的操作都是严格在logn时间内完成,效率非常高。set和multiset的区别是,set插入的元素不能相同,但是multiset可以相同,set默认是自动排序的,使用方法类似list。
1)vector
内部数据结构:数组。
在末尾增加或者删除元素所需时间与元素数目无关,在中间或者开头增加或者删除元素所需时间是随元素数目呈线性变化。
2):deque
内部数据结构是:数组。
随机访问每个元素,所需要的时间为常量。在开头和末尾增加元素所需时间与元素数目无关,在中间增加或删除所需时间随元素数目呈线性变化。
3)list
内部数据结构:双向环状链表。
不能随机访问一个元素,可双向遍历,在开头,末尾和中间的任何地方增加或者删除元素所需时间都是常量。
4)set
键和值相等且唯一。
元素默认按升序排列、
5)map
键唯一,值对应键。
元素默认按键的升序排列
关于迭代器失效的问题、小心使用STL中的erase。
//此段代码将crash
#include"stdafx.h"
#include
#include
using namespace std;
int main()
{
vector vect;
for(int i = 0; i < 10; i++ )
{
vect.push_back(i);
}
vector::iterator iter = vect.begin();
for(; iter != vect.end(); iter++ )
{
if( *iter % 2 == 0 )
{
vect.erase(iter);
}
}
return 0;
}
程序直接crash了。iter是指向vector这个容器中的某个元素,如果不是在for、while循环中,erase删除元素是没有问题的,但是如果在for、while循环中对容器迭代,删除其中符合条件的所有元素,就可能出现问题。vect.erase(iter)之后,iter及其后面的迭代器已经失效了,不应该再使用这些迭代器了,再执行iter++,其行为是未定义的。其它容器也会遇到迭代器失效的问题。
对于vector被删除元素的迭代器以及指向后面元素的迭代器全部失效。对于deque在首部或尾部删除元素则只会使指向被删除元素的迭代器失效,其它位置的插入和删除操作将使该容器所有迭代器失效。
对于list仅有指向被删除元素的迭代器失效。为什么不同容器迭代器失效情况有差别呢?这主要与各容器的数据结构有关。如下例子:
#include "stdafx.h"
#include
#include
#include