STL是Standard Template Library的简称,中文名为标准模板库。STL是基于模板设计的,可分为容器(containers)、迭代器(iterators)、空间配置器(allocator)、配接器(adapters)、算法(algorithms)、仿函数(functors)六个部分。通过STL的使用有效实现了算法与数据结构的分离。
顺序容器
基本介绍
顺序容器( sequential container)为程序员提供了控制元素存储和访问顺序的能力。主要有以下几种类型:
容器名 | 类型 | 特点 |
---|---|---|
vector | 可变大小数组 | 可通过下标快速访问元素,在尾部插入/删除速度快 |
list | 双向链表 | 只支持双向顺序访问,在任何位置插入/删除速度都很快 |
forward_list | 单向链表 | 只支持单向顺序访问,在任何位置插入/删除速度都很快 |
deque | 双端队列 | 可通过下标快速访问元素,在头尾位置插入/删除速度快 |
array | 固定大小数组 | 可通过下标快速访问元素,不能插入或删除元素 |
string | 字符串 | 用于保存字符串,可快速访问,在尾部插入/删除速度快 |
一般而言,容器保存元素的策略对容器操作的效率有着重大的影响。除了固定大小的array以外,其他容器都提供高效、灵活的内存管理。string和vector将元素保存在连续的内存空间中。由于元素是连续的,由元素的下标来计算地址很快。但是在这两种容器的中间位置插入或删除元素,就要对原有元素进行大量移动,导致效率低下。list和forward_list两个容器的设计目的是令容器任何位置的添加和删除操作都很快速。但作为代价,这两个容器不支持快速的随机访问,为了访问一个元素,只能遍历整个容器。deque同样像vector、string那样支持快速的元素访问,但在两端位置进行插入、删除速度同样较快,这点与list类似。每个容器都定义在一个与容器类型名相同的头文件中。容器都定义为模板类,在生成容器时必须提供额外的元素类型信息,如:
list<int> l1; //保存int的list
deque<double> d2; //保存double的deque
vector<vector<string>> v1; //保存vector的vector
迭代器
标准库中的迭代器允许我们方便地访问对容器元素进行访问,并且针对不同容器有着公共相同的接口。一个迭代器范围由一对迭代器表示,这一对迭代器被称为firs和last,其中last指向尾元素之后的位置,迭代器范围中的元素包含first所表示的元素以及从first开始直至last(但不包含last)之间的所有元素,被称为左闭合区间:[first,last)。
使用这种左闭合区间可以获得许多好处:
- 如果first与last相等,则范围为空。
- 如果first与last不行等,则范围内至少包含一个元素,first指向第一个元素
- 可以对first递增若干次,使得first==last。
这些性质意味着可以同以下代码安全地处理一个范围中的全部元素
while(first!=last){
*first = val;
first++}
显示地定义容器迭代器必须采用iterator容器成员,例如
list<string>::iterator it1=a.begin();
auto it2=a.end();
采用begin()和end()操作可以生成指向容器中第一个元素和为元素之后位置的迭代器。auto关键字可以让编译器自动对变量类型进行识别,省去很多麻烦。
容器初始化
每个容器类型都定义了一个一个默认构造函数,并可以通过多种构造方法创建容器对象,见下表:
容器初始化语句 | 介绍 |
---|---|
C c; | 采用默认构造函数构造空容器 |
C c1=c2; | c1初始化为c2的拷贝,c1和c2类型必须相同 |
C c1(c2); | c1初始化为c2的拷贝,c1和c2类型必须相同 |
C c(b,e); | c初始化为迭代器b和e指定范围内的元素,元素与容器元素类型必须相符 |
C c{a,b,c…}; | 列表初始化,列表中的元素必须与C的元素类型匹配 |
C c(n); | c包含有n个元素,这些元素进行值初始化 |
C c(n,t); | c包含有n个元素,这些元素的初始值为t |
以下是一些代码示例:
//采用列表初始化方法
list<string> authors ={"milton","Shakespeare","Austen"};
vector<const char*> articles={"a","an","the"};
list<string> list2(authors); //采用拷贝构造方法
deque<string> authList(authors);//!错误,类型不匹配
forward_list<string> words(articles.begin(),articles.end());// 采用迭代器构造
vector<int> ivec(10,-1);//构造10个int元素,每个元素初始化为1
list<string> svec(10,"hi!");//构造10个string元素,每个元素初始化为"hi!"
deque<string> svec(10);//构造10个元素,初始化为空string
容器添加与删除操作
除了array之外,所有容器都提供灵活的内存管理,在运行时可以动态添加或删除元素来改变容器大小。
向顺序容器添加删除元素 | |
---|---|
c.push_back(t) | 在c的尾部创建元素t,返回void |
c.push_front(t) | 在c的头部创建元素t,返回void |
c.insert(p,t); | 在迭代器p之前创建元素t,返回新元素迭代器 |
c.insert(p,n,t); | 在迭代器p之前创建n个元素t,返回新元素迭代器 |
c.insert(p,b,e); | 在迭代器p之前创建由迭代器b和e指定范围内的元素,返回第一个新元素迭代器 |
c.insert(p,il); | 在迭代器p之前创建由il元素值列表给定的元素,返回第一个新元素迭代器 |
c.pop_back(); | 删除c的尾元素,返回void |
c.pop_front(); | 删除c的头元素,返回void |
c.erase(p); | 删除迭代器p指向的元素,返回被删除元素之后的迭代器 |
c.erase(b,e); | 删除迭代器b和e指定范围内的元素,返回最后一个被删除元素之后的迭代器 |
c.clear(); | 删除c中的所有元素 |
其中所有的操作均不适用于固定大小数组array,vector和string 不支持push_front、pop_front操作,forward_list不支持push_back和pop_back操作。另外forward_list有自己专门版本的insert和erase操作,可以使效率更高。
容器大小操作
每个容器都含有三个与大小相关的操作,如下表所示:
容器大小操作 | |
---|---|
c.size(); | 返回容器中元素的数目 |
c.max_size(); | 返回一个容器可保存的最大元素数目 |
c.empty(); | size()为0是返回true,否则返回false |
关联容器
基本介绍
关联容器( associative container)与顺序容器不同,其中的元素是按关键字来保存于访问的。关联容器支持通过关键字的高效查找与提取。主要有以下几种类型:
容器名 | 类型 |
---|---|
map | 保存关键字和值,一个关键字只能出现一次 |
set | 保存关键字,一个给定的关键字只能出现一次 |
multimap | 保存关键字和值,一个关键字可以出现多次 |
multiset | 保存关键字,一个关键字可以出现多次 |
unordered_map | 用哈希函数组织的map |
unordered_set | 用哈希函数组织的set |
两个主要的关联容器类型就是map和set。map中的元素是一些关键字-值(key-value)对:关键字起到索引的作用,值则表示与索引相关联的数据。set中每个元素只包含一个关键字。一个map和set中的关键字必须是唯一的,容器multimap和multiset没有此限制,它们都允许多个元素具有相同的关键字。当从map中提取一个元素时,会得到一个pair类型的对象。pair是一个模板类型,保存两个名字为first和second的数据成员。map所使用的pair用first成员保存关键字,用second成员保存对应的值。
迭代器
当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type的值的引用。map和set同样支持顺序容器的begin和end操作,同样可以用这些函数来获取迭代器,进而遍历容器。
//获得一个指向首元素的迭代器
auto map_it = word_count.begin();
//比较当前迭代器和尾后迭代器
while (map_it != word_count.end()){
//解引用迭代器
cout<< map_it->first <<map_it->second<<endl;
//递增迭代器
++map_it;}
容器初始化
当定义一个map时,必须既指明关键字类型又指明值类型,一般将每个关键字-值对包围在花括号中:{key , value};而定义一个set时,只需指明关键字类型,因为set中没有值。每个关联容器都定义了一个默认构造函数,它创建一个指定类型的空容器。我们也可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器.
//采用列表初始化方法
map<string, size_t> word_count; //空容器
//采用列表初始化方法
set<string> exclude = {"the","but","and","or","an","a",
"The","But","And","Or","An","A"}
map<string, string> authors ={{"joyce", "James"},
{"Austen","Jane"},{"Dickens", "Charles"}};
容器添加与删除操作
关联容器不支持顺序容器的位置相关操作,例如push_front或push_front。原因是关联容器中元素是根据关键字存储的,这些操作对关联容器没有意义。以下定义了一些关联容器的插入、删除操作。
向关联容器添加删除元素 | |
---|---|
c.insert(v); | 创建一个元素v |
c.insert(b,e); | 创建由迭代器b和e指定范围内的元素 |
c.erase(k); | 删除关键字为k的元素,返回一个size_type指出删除元素数量 |
c.erase(b,e); | 删除迭代器b和e指定范围内的元素 |