STL是为标准化组件提供类模板进行规范型编程。STL是对C++技术的补充,具有通用性好,效率高,数据结构简单,安全机制完善等特点。STL是一些容器的集合,这些容器在算法的支持下使程序开发变得简单和高效。
序列容器
STL有许多容器,序列容器只提供插入功能,且元素都是有序的,但是未排序。序列容器包括vector向量,deque双端队列和list双向串行。
Vector向量类模板
向量vector是一种随机访问的数组类型,提供了对数组的快速,随机访问,以及在序列尾部快速、随机的插入和删除操作。它是大小可变的向量,在需要时可以改变其大小。
在使用vector之前,要先创建vector对象,创建方式有以下几种方法:
//方法一
std::vector<type> name;//创建名为name的空vector对象,这个容器能够容纳的数据类型为type
std::vector<int> intvector;//创建名为intvector的容器,可容纳的数据类型为int
//方法二
std::vector<type> name<size>;//在定义容器的同时初始化容器的大小
//方法三
std::vector<type>name(size,value);//在定义容器的同时初始化容器的大小,并将对象的初始值设为value
//方法四
std::vector<type>name(myvector);//该方法使用复制构造函数,用现有的向量myvector创建了一个vector对象
//方法五
std::vector<type>name(first,last);//创建了指定范围的向量,first和last分别代表起始位置和终止位置
通过具体实例来学习vector使用方法:
先介绍一下vector常用成员函数 (此处来源:( STL vector成员函数详解_ZxXiaer的博客-CSDN博客_vector成员函数)
vector::push_back(Type val) //将val添加到向量尾部
vector::pop_back() //将最后一个元素删除
iterator vector::begin() //返回指向第一个元素的迭代器
iterator vector::end() //返回指向最后一个元素之后的迭代器
vector::clear() //删除向量,并没有释放vector,还是占有capacity个空间,如果要释放空间,可使用 swap(vector<_Ty>()).
vector::at(n) //等效于vector[n] 返回第n个位置的引用
bool vector::empty() //向量是否为空
Type vector::front() //返回第一个元素的引用
Type vector::back() //返回最后一个元素的引用
vector::shrink_to_fit() //将capacity变为和size一样大
allocator_type vector::get_allocator() //得到分配器
代码实例:
#include<iostream>
#include<vector> //引入vector模板
#include<tchar.h>//tchar.h头文件提供了一个数据类型TCHAR, 这个类型在UNICODE环境下将映射为wchar_t类型;在ASCII环境下映射为char类型
using namespace std;
int main(int argc,_TCHAR*argv [])
{
vector<int> v1,v2;//定义了两个容器,容器的存储类型为int型,两个容器的名字分别为v1,v2
v1.reserve(10);//手动分配空间
v2.reserve(10);
cout<<"v1容量"<<v1.capacity()<<endl;//capacity函数的返回值代表当前情况下能够存储的元素个数
v1 = vector<int>(8,7);//方法三
int array [8] = {1,2,3,4,5,6,7,8};//定义数组
cout<<"v1当前各项:"<<endl;
for(int i = 0;i<v1.size();i++)//表示容器中所包含(已经存储)元素的个数
{
cout<<v1[i]<<" ";
}
cout<<endl;
----------------
v1容量10
v1当前各项:
7 7 7 7 7 7 7 7
----------------
cout<<"v2容量"<<v2.capacity()<<endl;
v2 = vector<int>(array,array+8);
cout<<"v2当前各项:"<<endl;
for(int i = 0;i<v2.size();i++)
{
cout<<v2[i]<<" ";
}
cout<<endl;
----------------
v2容量10
v2当前各项:
1 2 3 4 5 6 7 8
----------------
v1.resize(0);//resize()既修改capacity大小,也修改size大小
cout<<"v1的容量通过resize函数编程0"<<endl;
if(!v1.empty())
cout<<"v1容量"<<v1.capacity()<<endl;
else
cout<<"v1是空的"<<endl;
cout<<"将v1容量扩展为8"<<endl;
v1.resize(8);
cout<<"v1当前各项:"<<endl;
for(int i = 0;i<v1.size();i++)
{
cout<<v1[i]<<" ";
}
cout<<endl;
--------------------------
v1的容量通过resize函数编程0
v1是空的
将v1容量扩展为8
v1当前各项:
0 0 0 0 0 0 0 0
--------------------------
v1.swap(v2);
cout<<"v1与v2 swap了"<<endl;
cout<<"v1容量"<<v1.capacity()<<endl;
cout<<"v1当前各项:"<<endl;
for(int i = 0;i<v1.size();i++)
{
cout<<v1[i]<<" ";
}
cout<<endl;
----------------
v1与v2 swap了
v1容量8
v1当前各项:
1 2 3 4 5 6 7 8
----------------
v1.push_back(3);//将3添加到v1的尾部
cout<<"将3添加到v1的尾部"<<endl;
cout<<"v1容量"<<v1.capacity()<<endl;
cout<<"v1当前各项:"<<endl;
for(int i = 0;i<v1.size();i++)
{
cout<<v1[i]<<" ";
}
cout<<endl;
------------------
将3添加到v1的尾部
v1容量16
v1当前各项:
1 2 3 4 5 6 7 8 3
------------------
//erase函数可以用于删除vector容器中的一个或者一段元素,
//在删除一个元素的时候,其参数为指向相应元素的迭代器,
//而在删除一段元素的时候,参数为指向一段元素的开头的迭代器以及指向结尾元素的下一个元素的迭代器;
v1.erase(v1.end()-2);//vector::end()指向最后一个元素
cout<<"删除了倒数第二个元素"<<endl;
cout<<"v1容量"<<v1.capacity()<<endl;
cout<<"v1当前各项:"<<endl;
for(int i = 0;i<v1.size();i++)
{
cout<<v1[i]<<" ";
}
cout<<endl;
--------------------
删除了倒数第二个元素
v1容量16
v1当前各项:
1 2 3 4 5 6 7 3
--------------------
v1.pop_back();//弹出最后一个函数
cout<<"v1通过栈操作pop弹出最后一个元素"<<endl;
cout<<"v1容量"<<v1.capacity()<<endl;
cout<<"v1当前各项:"<<endl;
for(int i = 0;i<v1.size();i++)
{
cout<<v1[i]<<" ";
}
cout<<endl;
------------------------------
v1通过栈操作pop弹出最后一个元素
v1容量16
v1当前各项:
1 2 3 4 5 6 7
------------------------------
return 0;
}
注意:capacity()函数的输出结果跟预期不太符合,这是因为容器是线性表,内存空间是连续的,每次添加一个新元素,系统可能会自动分配额外的存储空间。因此不做过多探讨。
deque双端队列类模板
双端队列也是一种随机访问的数据类型,提供了在序列两端快速插入和删除操作的功能,可以修改自身大小,主要完成C++中数据结构中队列的功能。
在使用deque之前,要先创建deque对象,创建方式有以下几种方法:
//方法一
std::deque<type> name;//创建名为name的空deque对象,这个容器能够容纳的数据类型为type
std::deque<int> intdeque;//创建名为intdeque的容器,可容纳的数据类型为int
//方法二
std::deque<type> name<size>;//在定义容器的同时初始化容器的大小
//方法三
std::deque<type>name(size,value);//在定义容器的同时初始化容器的大小,并将对象的初始值设为value
//方法四
std::deque<type>name(myqueue);//该方法使用复制构造函数,用现有的向量mydeque创建了一个deque对象
//方法五
std::deque<type>name(first,last);//创建了指定范围内的双端队列,first和last分别代表起始位置和终止位置
deque与vector非常相似,有着和vector几乎一样的接口。不同的是deque的动态数组头尾都开放,因此能在头尾两端进行快速安插和删除。deque通常实作为一组独立区块,第一区块朝某方向扩展,最后一个区块朝另一个方向扩展。
双端队列类模板应用例子:
#include<stdio.h>
#include<iostream>
#include<deque>//引入deque模板
using namespace std;
int main()
{
deque<int> intdeque;//定义一个双端队列容器,存储类型为int型
//向容器中添加元素
intdeque.push_back(2);
intdeque.push_back(3);
intdeque.push_back(4);
intdeque.push_back(7);
intdeque.push_back(9);
cout<<"Deque old:"<<endl;
for(int i = 0;i<intdeque.size();i++)
{
cout<<intdeque[i]<<" ";
}
cout<<endl;
----------------
Deque old:
2 3 4 7 9
----------------
intdeque.pop_front();//弹出队首元素
intdeque.pop_front();
intdeque[1]=33;
cout<<"Deque new:"<<endl;
for(int i = 0;i<intdeque.size();i++)
{
cout<<intdeque[i]<<" ";
}
cout<<endl;
----------------
Deque new:
4 33 9
----------------
return 0;
}
链表类模板
链表类模板list是双向链表容器,它不支持随机访问,访问链表元素要指针从链表的某个端点开始进行遍历;插入和删除操作所花费的时间是固定的,和元素位置无关。list在任何位置插入和删除动作都很快,不想vector只能在末尾进行操作。
在使用list之前,要先创建list对象,创建方式有以下几种方法:
//方法一
std::list<type> name;//创建名为name的空list对象,这个容器能够容纳的数据类型为type
std::list<int> intlist;//创建名为intlist的容器,可容纳的数据类型为int
//方法二
std::list<type> name<size>;//在定义容器的同时初始化容器的大小
//方法三
std::list<type>name(size,value);//在定义容器的同时初始化容器的大小,并将对象的初始值设为value
//方法四
std::list<type>name(mylist);//该方法使用复制构造函数,用现有的向量mylist创建了一个list对象
//方法五
std::list<type>name(first,last);//创建了指定范围内的链表,first和last分别代表起始位置和终止位置
list与vector的操作相近,但是实现原理不同,执行效率也不一样。list的优点是插入的效率很高,缺点是不支持随机访问,即链表不能像数组一样进行随机访问。
对list各个元素的访问,通常使用迭代器。迭代器的使用方法类似于指针
代码示例:
#include<stdio.h>
#include<iostream>
#include<list>//引入list模板
#include<vector>
using namespace std;
int main()
{
cout<<"使用未排序存储0-9的数组初始化 list1"<<endl;
int arry[10] = {1,3,5,7,8,9,2,4,6,0};
list<int> list1(arry,arry+10);
cout<<"调用sort对list进行排序"<<endl;
list1.sort();
list<int>::iterator iter = list1.begin();//iter是一个迭代器,类似于指针的作用,指向第一个元素
//注意iter不支持‘+’运算符,但是可以自增
cout<<"通过迭代器访问第4个元素"<<endl;
for(int i = 0;i<3;i++)
{
iter++;
}
cout<<*iter<<endl;
list1.insert(list1.end(),13);//在链表末尾插入元素
cout<<"在链表末尾插入元素13"<<endl;
for(list<int>::iterator it = list1.begin();it!=list1.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
----------------------------------
使用未排序存储0-9的数组初始化 list1
调用sort对list进行排序
通过迭代器访问第4个元素
3
在链表末尾插入元素13
0 1 2 3 4 5 6 7 8 9 13
----------------------------------
关联式容器
关联式容器是STL提供的一种,其中元素都是经过排序的,它主要通过关键字的方式来提高查询效率。其中包括set,multiset,map,multimap和hash table。
set类模板
set类模板又称为集合类模板,一个集合对象像链表一样顺序的存储一组值。在一个集合中,集合元素即充当存储的数据,又充当数据的关键码。
创建set对象的方法:
//方法一
std::set<type,predicate> name;//创建名为name,存储数据类型为type的set对象
//predicate代表谓词,作用是用所指定的函数来对集合中的元素进行排序
std::set<int,std::less<int>> intset;//给整数创建一个空set对象,同时进行升序排序
//方法二
std::set<type,predicate> name(myset);//使用构造函数,从一个已经存在的集合myset中生成一个set对象
//方法三
std::set<type,predicate> name(first,last);//从一定范围的元素中根据多重指示器所指示的起始与终止位置创建一个集合
代码实例:
#include<stdio.h>
#include<iostream>
#include<set>//引入list模板
#include<vector>
using namespace std;
int main()
{
set<int> iSet;//创建一个整数集合
iSet.insert(1);//插入数据
iSet.insert(3);
iSet.insert(5);
iSet.insert(7);
iSet.insert(9);
cout<<"set:"<<endl;
set<int>::iterator it;
for(it=iSet.begin();it!=iSet.end();it++)
{
cout<<*it<<" ";
}
return 0;
}
------------
set:
1 3 5 7 9
------------
multiset类模板
多重集合类,即可以包含重复的数据。与集合类似,多重集合的元素既可以作为存储数据又可以作为数据的关键字。
创建multiset对象的方法:
//方法一
std::multiset<type,predicate> name;//创建名为name,存储数据类型为type的multiset对象
//predicate代表谓词,作用是用所指定的函数来对集合中的元素进行排序
std::multiset<int,std::less<int> > intset;//给整数创建一个空set对象,同时进行升序排序,注意std::less<int>后面有空格
//方法二
std::multiset<type,predicate> name(mymultiset);//使用构造函数,从一个已经存在的集合mymultiset中生成一个multiset对象
//方法三
std::multiset<type,predicate> name(first,last);//从一定范围的元素中根据多重指示器所指示的起始与终止位置创建一个集合
map类模板
map对象按顺序存储一组值,其中每个元素与一个检索关键码关联。map与set和multiset不同,set和multiset中元素即被作为存储的数据又被作为数据的关键值,而map类型中元素的数据和关键值是分开的。
创建map对象的方法:
//方法一
std::map<key,type,predicate> name;//创建名为name,存储数据类型为type的map对象
//predicate代表谓词,作用是用所指定的函数来对集合中的元素进行排序
std::map<int,int,std::less<int>> intset;//给整数创建一个空set对象,同时进行升序排序,注意std::less<int>后面有空格
//方法二
std::map<key,type,predicate> name(mymap);//使用构造函数,从一个已经存在的集合mymultiset中生成一个multiset对象
//方法三
std::map<key,type,predicate> name(first,last);//从一定范围的元素中根据多重指示器所指示的起始与终止位置创建一个集合
代码实例:
#include<stdio.h>
#include<iostream>
#include<map>//引入map模板
using namespace std;
int main()
{
map<int,char> cMap;
cMap.insert(map<int,char>::value_type(1,'B'));
cMap.insert(map<int,char>::value_type(2,'C'));
cMap.insert(map<int,char>::value_type(3,'D'));
cMap.insert(map<int,char>::value_type(4,'G'));
cMap.insert(map<int,char>::value_type(5,'F'));
cout<<"map"<<endl;
map<int,char>::iterator it;
for(it=cMap.begin();it!=cMap.end();it++)
{
cout<<(*it).first<<"->";
cout<<(*it).second<<endl;
}
return 0;
}
---------
map
1->B
2->C
3->D
4->G
5->F
---------
multimap类模板
与map相同之处是每一个元素都包含一个关键值以及与之联系的数据项,不同之处是多重映射可以包含重复的数据值,并且不能用"[]"操作符向多重映射中插入元素。
创建multimap对象的方法:
//方法一
std::multimap<key,type,predicate> name;//创建名为name,存储数据类型为type的multimap对象
//predicate代表谓词,作用是用所指定的函数来对集合中的元素进行排序
std::multimap<int,int,std::less<int> > intmap;//给整数创建一个空multimap对象,同时进行升序排序,注意std::less<int>后面有空格
//方法二
std::multimap<key,type,predicate> name(mymap);//使用构造函数,从一个已经存在的集合mymultiset中生成一个multiset对象
//方法三
std::multimap<key,type,predicate> name(first,last);//从一定范围的元素中根据多重指示器所指示的起始与终止位置创建一个集合
代码实例:
int main()
{
multimap<int,char> cMap;
cMap.insert(map<int,char>::value_type(1,'B'));
//多对一
cMap.insert(map<int,char>::value_type(2,'C'));
cMap.insert(map<int,char>::value_type(4,'C'));
cMap.insert(map<int,char>::value_type(5,'G'));
cMap.insert(map<int,char>::value_type(3,'F'));
cout<<"multimap"<<endl;
map<int,char>::iterator it;
for(it=cMap.begin();it!=cMap.end();it++)
{
cout<<(*it).first<<"->";
cout<<(*it).second<<endl;
}
return 0;
}
----------------
multimap
1->B
2->C
3->F
4->C
5->G
----------------
迭代器
迭代器相当于指向容器的指针,它可以向前移动,也可以向后移动。还有专门为输入输出准备的迭代器。
输出迭代器:
输出迭代器代码示例:可以进行递增和读取操作
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> intVect;//定义一个整数序列容器
for(int i = 0;i<10;i+=2)
{
intVect.push_back(i);
}
cout<<"Vect:"<<endl;
vector<int>::iterator it=intVect.begin();//什么样的容器就定义什么样的输出迭代器
while(it!=intVect.end())
{
cout<<*it++<<endl;
}
return 0;
}
---------------
Vect:
0
2
4
6
8
---------------
输入迭代器:
输出迭代器代码示例:可以进行递增,读取和比较操作。
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> intVect(5);//定义一个整数序列容器,容量是5
vector<int>::iterator in=intVect.begin();
*in++ = 1;
*in++ = 3;
*in++ = 5;
*in++ = 7;
*in = 9;
cout<<"Vect:";
vector<int>::iterator it=intVect.begin();
while(it!=intVect.end())
{
cout<<*it++<<" ";
}
cout<<endl;
return 0;
}
----------------
Vect:1 3 5 7 9
----------------
前向迭代器:
前向迭代器即可读,也可以用于写,不仅可以输入输出,还具有保存其值的功能,从而能够从迭代器原来的位置开始重新遍历。
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> intVect(5);//定义一个整数序列容器,容量是5
vector<int>::iterator it=intVect.begin();
vector<int>::iterator saveit=it;//saveit的作用是用于存储原来位置
*it++ = 1;
*it++ = 3;
*it++ = 5;
*it++ = 7;
*it = 9;
cout<<"Vect:";
while(saveit!=intVect.end())//从迭代器原来的位置开始重新遍历。
{
cout<<*saveit++<<" ";
}
cout<<endl;
return 0;
}
----------------
Vect:1 3 5 7 9
----------------
双向迭代器:
双向迭代器即可读,也可以用于写,与前向迭代器类似,只是双向迭代器可以做递增和递减操作。
代码示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> intVect(5);//定义一个整数序列容器,容量是5
vector<int>::iterator it=intVect.begin();
vector<int>::iterator saveit=it;//saveit的作用是用于存储原来位置
*it++ = 1;
*it++ = 3;
*it++ = 5;
*it++ = 7;
*it = 9;
cout<<"Vect:"<<endl;
while(saveit!=intVect.end())//从迭代器原来的位置开始重新遍历。
{
cout<<*saveit++<<" ";//自增
}
cout<<endl;
do
cout<<*--saveit<<" ";//自减
while(saveit!=intVect.begin());
cout<<endl;
return 0;
}
-------------
Vect:
1 3 5 7 9
9 7 5 3 1
-------------
随机访问迭代器
随机访问迭代器是最强大的迭代器类型,不仅具有双向迭代器所有功能,还能使用指针的算术运算和所有比较运算。
代码示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> intVect(5);//定义一个整数序列容器,容量是5
vector<int>::iterator it=intVect.begin();
*it++ = 1;
*it++ = 3;
*it++ = 5;
*it++ = 7;
*it = 9;
cout<<"Vect Old:"<<endl;
for(it=intVect.begin();it!=intVect.end();it++)
{
cout<<*it<<" ";
}
it = intVect.begin();
*(it+2) = 100;
cout<<endl;
cout<<"Vect:";
for(it=intVect.begin();it!=intVect.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
-----------------
Vect Old:
1 3 5 7 9
Vect:
1 3 100 7 9
-----------------