STL是C++标准库的核心,STL组件中最关键的是容器、迭代器和算法。容器是用来管理某类对象的集合;迭代器为所有各式各样的容器提供了一组共通的接口;算法用于处理集合内的元素。
STL的基本概念是将数据和操作分离,数据由容器加以管理,操作则由可定制的算法定义,迭代器在二者之间充当粘合剂。
容器
- 序列式容器 array、vector、deque双端队列、list双向链表、forward_list单向链表
- 关联式容器 set、multiset、map、multimap
- 无序容器 unordered_set、unordered_multiset、unordered_map、unordered_multimap
关联式容器是由二叉树实现出来,可以快速的找出一个具有某特定value的元素。set元素依据value自动排序,元素不允许重复;multiset元素依据value自动排序,元素允许重复;map每个元素都是key/value对,其中key为排序准则,key不允许重复;multimap每个元素都是key/value对,其中key为排序准则,key允许重复.
#include "stdafx.h" #include <iostream> #include <map> #include <string> using namespace std; int main() { multimap<int, string> coll; coll = { {5,"tagged"},{2,"a"},{1,"this"},{4,"of"},{6,"strings"},{1,"is"},{3,"multimap"} }; for (const auto& elem : coll) { cout << elem.second << " "; //--> this is a multimap of tagged strings } cout << endl; return 0; }
无序容器常以hash table实现出来,无序容器的主要优点是,当你打算查找出一个某特定值的元素,其速度甚至可能快过关联式容器。
容器适配器Container Adapter
容器适配器是根据基本容器实现而成,用于满足特定的需求。包括stack、queue、priority queue
迭代器Iterator
迭代器是一个可遍历STL容器全部或者部分元素的对象。迭代器具有遍历复杂数据结构的能力,每一种容器都必须提供自己的迭代器,事实上每一种容器都将迭代器以嵌套的形式定义于class内部。因此各种迭代器的接口虽然相同,但其内部实现确各自不同。
所有容器类型都提供了一些基本的成员函数,使得可以取得迭代器并以之遍历容器元素。
begin() 返回一个迭代器,指向容器第一个元素
end() 返回一个迭代器,返回容器最后一个元素的下一个元素(尾后迭代器)
空区间的begin()等于end()
任何容器都定义有两种迭代器类型:
- container::iterator 以“读写”模式遍历元素
- container::const_iterator 以“只读”方式遍历元素
自C++11起,容器提供了cbegin()和cend(),它们返回类型为container::const_iterator的对象。
算法 Algorithm
算法并非是容器的成员函数,而是一种搭配迭代器使用的全局函数。这样做的优势在于所有算法只需要实现一份,就可以对所有容器运作。为了操作容器元素的某个子集,需要将区间首尾作为参数传入算法,调用者需要确保指定的区间是有效的。所有算法处理的都是半开区间。
min_element() 返回容器最小元素的位置
max_element() 返回容器最大元素的位置
sort() 排序
find() 查找元素,查找成功则返回一个迭代器指向目标元素,查找失败则返回第二个实参指示的区间末端
reverse() 将区间内元素反转
有些算法需要处理多个区间,通常必须设定第一个区间的起点和终点,其他区间只需设定起点即可,终点通常可由第一个区间的元素数量推导出来。必须确保第二个(其他)区间的元素个数至少和第一个区间元素个数一样多。可以(1)确保目标区间内有足够元素,(2)使用插入迭代器
迭代器之适配器 Iterator Adapter
1. Insert Iterator 安插型迭代器
Insert Iterator可以使算法以插入而非覆写的方式运作,促使目标区间的大小按照需要增长。有三种Insert Iterator:
1 #include "stdafx.h" 2 #include <iostream> 3 #include <list> 4 #include <string> 5 #include <vector> 6 #include <set> 7 #include <deque> 8 #include <iterator> 9 #include <algorithm> 10 using namespace std; 11 12 int main() 13 { 14 list<int> coll1 = { 1,2,3,4,5,6,7,8,9 }; 15 //copy elements by appending them 16 vector<int> coll2; 17 copy(coll1.cbegin(), coll1.cend(), back_inserter(coll2)); 18 for (auto elem:coll2) 19 { 20 cout << elem << " "; 21 } 22 cout << endl; 23 //copy elements by inserting them at the front 24 deque<int> coll3; 25 copy(coll1.cbegin(), coll1.cend(), front_inserter(coll3)); 26 for (auto elem : coll3) 27 { 28 cout << elem << " "; 29 } 30 cout << endl; 31 //works for associative collections 32 set<int> coll4; 33 copy(coll1.cbegin(), coll1.cend(), inserter(coll4, coll4.begin())); 34 for (auto elem : coll4) 35 { 36 cout << elem << " "; 37 } 38 cout << endl; 39 return 0; 40 }
back_inserter 安插于容器最末端,其内部是调用push_back()
front_inserter 安插于容器最前端,其内部调用push_front()
inserter 内部调用insert(),在“初始化时接收的第二个实参位置的前方插入元素”
2. Stream Iterator 串流迭代器
3. Reverse Iterator 反向迭代器
所有提供双向或者随机访问迭代器的容器(即forward_list之外的所有序列式容器和所有关联容器)都可以通过其成员函数rbegin()和rend()产生一个反向迭代器,通过crbegin()和crend()产生只读反向迭代器。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "stdafx.h" #include <iostream> #include <vector> #include <iterator> #include <algorithm> using namespace std; int main() { vector<int> coll; for (int i = 1;i <= 9;++i) { coll.push_back(i); } copy(coll.crbegin(), coll.crend(), ostream_iterator<int>(cout, " ")); //9 8 7 6 5 4 3 2 1 cout << endl; return 0; }
4. Move Iterator 搬移迭代器
函数作为算法的实参
有些算法可以接受用户自定义的函数作为实参,以提高算法的弹性和能力。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "stdafx.h" 2 #include <iostream> 3 #include <vector> 4 #include <iterator> 5 #include <algorithm> 6 using namespace std; 7 8 void print(int elem) 9 { 10 std::cout << elem << " "; 11 } 12 int main() 13 { 14 vector<int> coll; 15 for (int i=1;i<=9;++i) 16 { 17 coll.push_back(i); 18 } 19 //print all elements 20 for_each(coll.cbegin(), coll.cend(), print); //1 2 3 4 5 6 7 8 9 21 cout << endl; 22 return 0; 23 }
使用判断式 Predicate
predicate是一种特殊的辅助函数,其返回bool值,常被用于指定作为排序准则或者查找准则。
Lambda表达式
Lambda表达式完整声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
其中:
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
- function body:函数体
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "stdafx.h" #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; int main() { vector<int> coll = { 3,2,5,4,1,8}; sort(coll.begin(), coll.end(), [](int a, int b)->bool {return a < b;}); for (auto elem : coll) { cout << elem << " "; //1 2 3 4 5 8 } cout << endl; return 0; }
函数对象Function Object(仿函数functor)
1 #include "stdafx.h" 2 #include <iostream> 3 #include <vector> 4 #include <string> 5 #include <algorithm> 6 using namespace std; 7 8 class PrintInt 9 { 10 public: 11 void operator()(int elem) const //使用operator()定义函数对象 12 { 13 std::cout << elem << " "; 14 } 15 }; 16 int main() 17 { 18 vector<int> coll; 19 for (int i = 1;i <= 9;++i) 20 { 21 coll.push_back(i); 22 } 23 for_each(coll.cbegin(), coll.cend(), PrintInt()); //1 2 3 4 5 6 7 8 9 24 cout << endl; 25 return 0; 26 }
C++标准库内有若干预定义的函数对象,涵盖了许多基础运算。定义于头文件functional中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "stdafx.h" #include <iostream> #include <deque> #include <string> #include <algorithm> #include <functional> using namespace std; int main() { deque<int> coll = { 1,2,3,5,7,11,13,17,19 }; for (auto elem : coll) { cout << elem << " "; } cout << endl; transform(coll.cbegin(), coll.cend(), coll.begin(), negate<int>()); for (auto elem : coll) { cout << elem << " "; } cout << endl; //square elements in coll transform(coll.cbegin(), coll.cend(), //first source coll.cbegin(), //second source coll.begin(), //destination multiplies<int>()); //operation for (auto elem : coll) { cout << elem << " "; } cout << endl; return 0; }
Binder 将预定义的函数对象和其他数值结合为一体。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "stdafx.h" #include <iostream> #include <set> #include <deque> #include <iterator> #include <algorithm> #include <functional> using namespace std; using namespace std::placeholders; //占位符 int main() { set<int, greater<int>> coll1 = { 1,2,3,4,5,6,7,8,9 }; deque<int> coll2; for (auto elem : coll1) { cout << elem << " "; } //9 8 7 6 5 4 3 2 1 cout << endl; //transform elements to coll2 by multiplying with 10 transform(coll1.cbegin(), coll1.cend(), back_inserter(coll2), bind(multiplies<int>(), _1, 10)); // _1占位符 for (auto elem : coll2) { cout << elem << " "; } //90 80 70 60 50 40 30 20 10 cout << endl; //replace 70 with 42 replace_if(coll2.begin(), coll2.end(), bind(equal_to<int>(), _1, 70), 42); for (auto elem : coll2) { cout << elem << " "; } //90 80 42 60 50 40 30 20 10 cout << endl; return 0; }
bind()将低层的函数对象和占位符合成高层的函数对象,占位符为“带有前缀下划线”的数值标识符。
bind(multiplies<int>(),_1,10) 便定义出一个函数对象,将第一个实参乘以10