STL标准库
1. STL概念
为了建立数据结构和算法的一套标准,并且降低他们之间的耦合关系,以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability),诞生了STL
STL(Standard Template Library,标准模板库),是惠普实验室开发的一系列软件的统称。现在主要出现在 c++中,但是在引入 c++之前该技术已经存在很长时间了。
STL(Standard Template Library)标准模板库,在我们 c++标准程序库中隶属于 STL 的占到了 80%以上。
六大组件:
容器:数据结构,用于存放数据【类模板】
算法:操作数据的各种功能,如删除、排序、查询等【函数模板】
迭代器:算法借助迭代器操作数据(主要读数据) 【各种运算符重载】
仿函数:算法的某种策略,增强算法的功能。【()运算符重载】
适配器:用于扩展容器、算法、迭代器的接口
空间管理器:负责空间的配置与管理 【类模板】
STL优点:
1)STL是C++的一部分,不需要安装外部库
2)STL将数据和操作分离
3)STL具有高可重用性,高性能,高移植性,跨平台的优点
高可重用性:采用了模板类和模板函数
高性能:可以高效地从大量的数据中快速查找,如map采用红黑树的结构
高移植性:只要存在C++的编译环境的操作系统,都可以编译和运行STL模块。
STL 之父 Alex Stepanov 亚历山大·斯特潘诺夫(STL 创建者)。
2. STL三大组件的基本用法
容器(数据结构,vector,set,map,queue等)、算法(插入数据、删除数据、修改数据、排序等)、迭代器(算法操作容器)
2.1 容器
容器:用于存放数据
常用的数据结构
数组(array) 链表(list) 树(tree) 栈(stack) 队列(queue) 集合(set) 映射表(map)
根据数据在容器中的排列特性:序列式容器、关联式容器
序列式容器:
强调值的排序,每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。如:vector,deque/queue,list;
关联式容器:
非线性,更准确的说是二叉树结构,各元素之间没有严格的物理上的顺序关系;在数据中选择一个关键字key,这个key对数据起到索引的作用,方便查找。如:set/multiset,map/multimap容器
【注】容器可以嵌套容器
2.2 算法
算法(Algorithm):用于解决问题
STL收录的算法经过了数学上的效能分析与证明,是极具复用价值的,包括常用的排序,查找等。特定的算法往往搭配特定的数据结构,算法与数据结构相辅相成。
算法分为:质变算法和非质变算法
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝、替换、删除等
非质变算法:是指运算过程中不会更改区间内的元素内容。如查找、统计、求极值等。
2.3 迭代器
迭代器(iterator)是一种抽象的设计概念,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式
简之,迭代器是依次遍历容器中所有的元素
迭代器的种类:
输入迭代器:只读数据,支持++、==、!=
输出迭代器:只写数据,支持++
向前迭代器:读写数据(向前),支持++、==、!=
双向迭代器:读写数据(向前、向后),支持++、--
随机迭代器:提供读写操作(跳跃式访问任意位置),支持++、--、[]、-n、<、<=、>、>=
3. STL常见容器的应用
3.1 string 容器
string也是字符串的类型,可以理解它是用于存放字符的容器
3.1.1 string和c风格字符串对比
1)char*是一个指针,string是一个类
2)string封装了很多实用的成员方法
查找find,拷贝copy,删除delete,替换replace,插入insert
3)不用考虑内存释放和越界
string管理char*所分配的内存,每一次string的赋值,取值都由string类负责维护,不用担心赋值越界和取值越界等
3.1.2 string容器api
//构造函数
string(); //创建一个空的字符串 string str;
string(const string& str); //使用一个string对象初始化另一个string对象
string(const char* s); //使用字符串s初始化
string(int n,char c); //使用n个字符c初始化
//赋值
string& operator=(const char* s); //char*类型字符串 赋值给当前的字符串
string& operator=(const string &s); //把字符串s赋值给当前的字符串
string& operator=(char c); //字符赋值给当前的字符串
string& assign(const char*s);//把字符串s赋值给当前的字符串
string& assign(const char*s,int n);//把字符串s的前n哥字符赋给当前的字符串
string& assign(const string &s);//把字符串s赋给当前字符串
string& assign(int n,char c);//用n个字符c赋给当前字符串
string& assign(const string &s,int start,int n);//将s从start开始n个字符赋值给字符串
//取值
char& operator[](int n);//通过[]方式取字符
char& at(int n);//通过at方法取字符
//字符串拼接
string& operator+=(const string& str); //重载+=操作符
string& operator+=(const char* str); //重载+=操作符
string& operator+=(const char c);//重载+=操作符
string& append(const char*s);//把字符串s连接到当前字符串结尾
string& append(const char*s,int n);//把字符串s的前n个字符连接到当前字符串结尾
string& append(const string &s); //同operator+=()
string& append(const string &s,int pos,int n);//把字符串s从pos的n个字符连接到当前字符串结尾
string& append(int n,char c);//在当前字符串结尾添加n个字符c
//查找与替换
int find(const string& str,int pos=0)const;//查找str第一次出现位置,从pos开始查找,如果未查找到返回-1
int find(const char* s,int pos=0)const;//查找s第一次出现位置,从pos开始找
int find(const char* s,int pos,int n)const;//从pos位置查找s的前n个字符第一次位置
int find(const char c,int pos=0)const;//从pos位置查找c第一次出现位置
int rfind(const string& str,int pos=npos)const;//查找str最后一次位置,从pos开始查找
int rfind(const char* s,int pos=npos)const;//从pos开始查找s最后一次位置
int rfind(const char* s,int pos,int n)const;//从pos查找s的前n个字符最后一次位置
int rfind(const char c,int pos=0)const;//查找字符c最后一次出现位置
string& replace(int pos,int n,const string& str);//替换从pos开始n个字符为字符串str
string& replace(int pos,int n,const char* s);//替换从pos开始的n个字符为字符串s
//比较,返回值 0;相等,1;大于s,-1;小于s
int compare(const string &s)const;//与字符串s比较
int compare(const char *s)const;//与字符串s比较
//截取子字符串
string substr(int pos=0,int n=npos)const;//返回由pos开始的n个字符组成的字符串
//插入与删除
string& insert(int pos,const char* s);//插入字符串
string& insert(int pos,const string& str);//插入字符串
string& insert(int pos,int n,char c);//在指定位置插入n个字符c
string& erase(int pos,int n=npos);//删除从pos开始的n个
//转成char*
const char* c_str() 将当前字符串转成char*
//获取字符串的长度
int size();
3.2 vector容器
3.2.1 vector与数组的区别
vector的结构类同于数组,数组是静态的,在定义时确定的数组的大小;而vector是动态的,添加元素时如果空间不足时,则会自动扩容器(2^n);整体来说,vector比较灵活的,而且vector是类模板,可以存放任意类型的元素
3.2.2 vector迭代器
vector维护一个线性空间(线性连续空间),vector迭代器所需要的操作行为是(*,->,++,--,+,-,+=,-=)运算符重载等。vector支持随机存取,vector提供的是随机访问迭代器(Random Access Iterator),迭代器中的元素即为vector容器中的元素。
【注】vector扩容之后,原迭代器则无效,需要重新获取迭代器
所谓动态增加大小,并不是在原空间之后续接新空间(因为无法保证原空间之后尚有可配置的空间),而是一块更大的内存空间,然后将原数据拷贝新空间,并释放空间。
因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。
3.2.3 vector容器api
//构造函数
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(),v.end());//将v[begin,end())区间中的元素拷贝给本身
vector(n,elem);//构造函数将n个elem拷贝给本身
vector(const vector &vec);//拷贝构造函数
//赋值操作
assign(beg,end);//将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem); //将n个elem拷贝赋值给本身
vector& operator=(const vector &vec);//重载等号操作符
swap(vec);//将vec与本身的元素互换
//大小操作
int size(); //返回容器中元素的个数
bool empty();//判断容器是否为空,返回bool值(0,1)
void resize(int num);//重新指定容器的长度为num,若容器边长,则以默认值填充新位置。若容器变短,则末尾超出容器长度的元素被删除
void resize(int num,elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
int capacity(); //容器的容量
void reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问。
//取值操作
at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常
operator[](int idx);//返回索引idx所指的数据,越界时,运行直接报错
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
//插入和删除
insert(const_iterator pos,int count,T ele);//迭代器指向位置pos插入count个元素ele
push_back(ele); //尾部插入元素ele
pop_back(); //删除最后一个元素
erase(const_iterator start,const_iterator end);//删除迭代器从start到end之间的元素,删除[start,end)区间的所有元素
erase(const_iterator pos);//删除迭代器指向的元素
clear(); //删除容器中所有元素
巧用swap收缩内存空间
resize()+swap()实现vector收缩内存空间
resize(n);
vector<T>(v).swap(v);
3.3 deque容器
3.3.1 deque基本概念
vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。
deque容器和vector容器最大的差异:
1)在于deque允许使用常数项时间对头端进行元素的插入和删除操作
2)在于deque没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来
3)deque没有必要要提供所谓的空间保留(reserve)功能
4)虽然deque容器也提供了Random Access Iterator,但是它的迭代器并不是普通的指针,其复杂度和vector不是一个量级,这当然影响各个运算的层面。因此,除非有必要,我们应该尽可能的使用vector,而不是deque
5)对deque进行的排序操作,为了最高效率,可将deque先完整的复制到一个vector中,对vector容器进行排序,再复制回deque。
3.3.2 deque实现原理
逻辑上,deque容器是连续的空间,是由一段一段的定量的连续空间构成。一旦有必要在deque前断或者尾端增加新的空间,便配置一端连续定量的空间,串接在deque的头端或者尾端。
deque最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口。
既然deque是分段连续内存空间,那么就必须有中央控制(map实现的),维持整体连续的假象,数据结构的设计及迭代器的前进后退操作颇为繁琐
中央控制:连续小空间,由map实现,存放是地址,地址指向的另一个连续空间为缓冲区。
缓冲区:是deque的存储空间的主体。
3.3.3 常用API
//构造函数
deque<T> deqT;//默认构造形式
deque(beg,end);//构造函数将[beg,end)区间中的元素拷贝给本身
deque(n,elem);//构造函数将n个elem拷贝给本身
deque(const deque &deq);//拷贝构造函数
//赋值操作
assign(beg,end);//将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem);//将n个elem拷贝赋值给本身
deque& operator=(const deque &deq);//重载等号运算符
swap(deq);//将deq与本身的元素互换
//大小操作
deque.size();//返回容器中元素的个数
deque.empty();//判断容器是否为空
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
deque.resize(num,elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
//双端插入和删除
void push_back(elem);//在容器尾部添加一个数据
void push_front(elem);//在容器头部插入一个数据
void pop_back();//删除容器最后一个数据
void pop_front();//删除容器第一个数据
//数据存取
at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range
operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,会直接出错
front();//返回第一个数据
back();//返回最后一个
//插入操作
void insert(iterator pos,T elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置
void insert(iterator pos,int n,T elem);//在pos位置插入n个elem数据,无返回值
void insert(iterator pos,iter_beg,iter_end);//在pos位置插入[beg,end)区间的数据,无返回值
//删除操作
clear();//移除容器的所有数据
iterator erase(iterator beg,iterator end);//删除[beg,end)区间的数据,返回下一个数据的位置
iterator erase(iterator pos);//删除pos位置的数据,返回下一个数据的位置
3.4 stack容器
3.4.1 stack基本概念
stack是一种先进后出(First in Last Out,FILO)的数据结构,它只有一个出口,stack容器允许新增元素、移除元素、取得栈顶元素,但是除了最顶端外,没有任何其他方法可以存取stack的其他元素,换言之,stack不允许有遍历行为
3.4.2 stack没有迭代器
stack 所有元素的进出都必须符合”先进后出”的条件,只有 stack 顶端的元素,才有机会被外界取用。Stack 不提供遍历功能,也不提供迭代器。
3.4.3 常用API
//构造函数
stack<T> stkT;//stack 采用模板类实现,stack对象的默认构造形式
stack(const stack &stk);//拷贝构造函数
//赋值操作
stack& operator=(const stack &stk);//重载等号操作符
//数据存取
void push(elem);//向栈顶添加元素
void pop();//从栈顶移除第一个元素
T pop();//返回栈顶元素
//大小操作
empty();//判断堆栈是否为空
size();//返回堆栈的大小
3.5 queue容器
3.5.1 queue基本概念
queue是一种先进先出(First in first out,FIFO)的数据结构,它有两个出口,queue容器允许从一端新增元素,从另一端移除元素
3.5.2 queue没有迭代器
queue所有元素的进出都必须符合"先进先出"的条件,只有queue的顶端元素,才有机会被外界取用。queue不提供遍历功能,也不提供迭代器
3.5.3 常用API
//构造函数
queue<T> queT; //queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que); //拷贝构造函数
//存取、插入和删除
void push(elem); //往队尾添加元素
void pop(); //从队头移除第一个元素
T back(); //返回最后一个元素
T front(); //返回第一个元素
//赋值与大小
queue& operator=(const queue &que);//重载等号操作符
empty(); //判断队列是否为空
size(); //返回队列的大小
3.6 list容器
3.6.1 list概念
list(链表)是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成
每个结点包含两个部分:
1)存储数据元素的数据域
2)存储下一个结点地址的指针域
list与vector的比较:
1)相对于vector的连续线性空间,list就显得负责许多,每次插入或者删除一个元素,就是配置或者释放一个元素的空间,不浪费多余的空间,且插入与移除元素的操作是常数时间(稳定)。
2)list和vector是两个最常被使用的容器,但list是由双向链表实现的
3)list插入操作和删除操作都不会造成原有list迭代器的失效
list特点:
1)采用动态存储分配,不会造成内存浪费和溢出
2)链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
3)链表灵活,但是空间和时间额外耗费较大
3.6.2 list的迭代器
list不能像vector一样以普通指针作为迭代器,因为其节点不能保证在同一块连续的内存空间上。list迭代器必须有能力指向list的节点,并有能力进行正确的递增、递减、取值、成员存取操作。递增时指向下一个节点,递减时指向上一个节点,取值时取的是节点的数据值,成员取用时取的是节点的成员。另外,list是一个双向链表,迭代器必须能够具备前移、后移的能力,所以list容器提供的是双向的迭代器(bidrectional iterators)
list有一个重要的性质,插入和删除都不会造成原有list迭代器失效
【注】list的迭代器,不支持+n
操作
list容器不仅是一个双向链表,而且还是一个循环的双向链表
3.6.3 常用API
//构造函数
list<T> lstT; //list采用模板类实现,对象的默认构造形式
list(beg,end); //构造函数将[beg,end)区间中的元素拷贝给本身
list(n,elem); //构造函数将n个elem拷贝给本身
list(const list &lst); //拷贝构造函数
//插入和删除
push_back(elem); //在容器尾部加入一个元素
pop_back(); //删除容器中最后一个元素
push_front(elem); //在容器开头插入一个元素
pop_front(); //从容器开头移除第一个元素
iterator insert(pos,elem); //在pos位置插elem元素的拷贝,返回新数据的位置
void insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值
void insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
clear(); //移除容器的所有数据
iterator erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
iterator erase(pos); //删除pos位置的数据,返回下一个数据的位置
remove(elem); //删除容器中所有与elem值匹配的元素
//大小
size(); //返回容器中元素的个数
empty(); //判断容器是否为空
resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
resize(num,elem);
//赋值
assign(beg,end); //将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem); //将n个elem拷贝赋值给本身
list& operator=(const list &lst); //重载等号操作符
swap(lst); //将lst与本身的元素互换
//读取
front(); //返回第一个元素
back(); //返回最后一个元素
//反转和排序
reverse(); //反转链表
sort(); //list排序
3.7 set/multiset容器
3.7.1 set概念
set(集合)的特性是所有元素都会根据元素的键值自动被排序
set的元素即是键值(key)又是实值(value),不允许两个元素有相同的键值
set的iterator是一种const_iterator,不允许修改set的键值
set拥有和list某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前的迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外。
3.7.2 set数据结构
multiset的特性及用法和set完全相同,唯一的差别在于它允许键值重复。set和multiset的底层实现是红黑树。
3.7.3 常用API
//构造函数
set<T> st; //set默认构造函数
multiset<T> mst; //multiset默认构造函数
set(const set &st); //拷贝构造函数
set(begin,end); //复制[begin,end)区间的数据到当前的集合中
//赋值和大小
set& operator=(const set &st); //重载等号操作符
swap(st); //交换两个集合容器
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
// 插入和删除
insert(elem); //在容器中插入元素
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(elem); //删除容器中值为elem的元素
//查找
find(key); //查找键key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end();
count(key); //查找键key的元素个数,针对multiset
lower_bound(keyElem); //返回第一个key>=keyElem元素的迭代器
upper_bound(keyElem); //返回第一个key>keyElem元素的迭代器
equal_range(keyElem); //返回容器中key与keyElem相等的上下限的两个迭代器
set排序规则
set自动排序,但可以改变它的排序规则,默认从小到大
自动排序规则,可以使用struct或class,声明bool operator()(v1,v2)
仿函数重载
在定义集合时,指定排序规则(类):
set<数据元素的泛型,排序规则类型> s;
3.7.5 对组(pair)
对组(pair)将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有属性first和second访问
类模板:template <class T1,class T2> class pair
用法一:
pair<string,int> pair1(string("name"),20);
cout<<pair1.first<<endl;
cour<<pair1.second<<endl;
用法二:
pair<string,int> pair2=make_pair("name",30);
cout<<pair2.first<<endl;
cout<<pair2.second<<endl;
用法三:
pair<string,int> pair3=pair2; //拷贝构造函数
cout << pair3.first<<endl;
cout << pair3.second<<endl;
3.8 map/multimap容器
3.8.1 map概念
map的特性是所有元素都会根据元素的键值自动排序
map所有的元素都是pair,同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值【键值是唯一的】
multimap和map的操作类型,唯一区别multimap键值可重复
map和multimap都是以红黑树为底层实现机制
【注】map迭代器的元素是pair对组,通过pair对象获取键值(first)和实值(second)
3.8.2 常用API
//构造函数
map<T1,T2> mapTT; //map默认构造函数
map(const map &mp); //拷贝构造函数
map(begin,end); //复制[begin,end)区间的pair对到当前map中
//赋值和大小
map& operator=(const map &mp); //重载等号操作符
swap(mp); //交换两个集合
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
//插入
insert(pair<...>(...)); //往容器中插入元素,返回pair<iterator bool>
insert(make_pair(...))
insert(map<T1,T2>::value_type(...));
T2& operator[](T1 &key);
//删除
clear(); //删除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器。
erase(keyElem); //删除容器中key为keyElem的对
//查找
find(key); //查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
count(keyElem); //返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1,对multimap来说,值可能大于1
lower_bound(keyElem); //返回第一个key>=keyElem元素的迭代器
upper_bound(keyElem); //返回第一个key>keyElem元素的迭代器
euqal_range(keyElem); //返回容器中key与keyElem相等的上下限的两个迭代
3.9 STL容器使用时机
3.9.1 结构与操作比较
3.9.2 使用场景
- vector:如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述
- deque:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢
- list:比如公交车乘客的存储,随时有可能有乘客下车,支持频繁的不确定位置元素的移除插入
- set:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列
- map:比如按ID号存储十万个用户,想要快速通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。
4. STL算法
4.1 函数对象
重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载"()"操作符,使得类对象可以像函数那样调用
【注意】
1. 函数对象(仿函数)是一个类,不是一个函数
2. 函数对象(仿函数)重载了"()"操作符使得它可以像函数一样调用
函数对象分类:
一元仿函数(unary_functor),重载的operator()要求获取一个参数
二元仿函数(binary_functor),重载的operator()要求获取两个参数
函数对象的作用:
STL操作的算法往往都有两个版本,其中一个版本表现出最常用的某种运算,另一版本则允许用户通过template参数的形式来指定所要采取的策略
对于template 参数的形式
意思,如指定set的排序规则
set<A,sortA> s;
【总结】
1. 函数对象通常不定义构造函数和析构函数,所以在构造和析构时不会发生任何问题,避免了函数调用的运行时问题
2. 函数对象超出普通函数的概念,函数对象可以有自己的状态
3. 函数对象可内联编译、性能好,用函数指针几乎不可能
4. 模板函数对象使函数对象具有通用性,这也是它的优势之一
4.2 谓词
谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(即仿函数),依据函数接收的参数又分为一元谓词
和二元谓词
等,谓词可作为一个判断式。
4.3 内建函数对象
引入头文件
<algorithm>
STL内建了一些函数对象,分为算数函数对象、关系运算类函数对象、逻辑运算类仿函数。这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能
4.3.1 算数类函数对象
6个算数类函数对象,除了negate是一元运算,其他都是二元运算
template<class T> T plus<T>//加法仿函数
template<class T> T minus<T>//减法仿函数
template<class T> T multiplies<T>//乘法仿函数
template<class T> T divides<T>//除法仿函数
template<class T> T modulus<T>//取模仿函数
template<class T> T negate<T>//取反仿函数
4.3.2 关系运算类函数对象
6个关系运算类函数对象,每一种都是二元运算
template<class T> bool equal_to<T>//等于
template<class T> bool not_equal_to<T>//不等于
template<class T> bool greater<T>//大于
template<class T> bool greater_equal<T>//大于等于
template<class T> bool less<T>//小于
template<class T> bool less_equal<T>//小于等于
4.3.3 逻辑运算类运算函数
逻辑运算类运算函数,not为一元运算,其余为二元运算
template<class T> bool logical_and<T>//逻辑与
template<class T> bool logical_or<T>//逻辑或
template<class T> bool logical_not<T>//逻辑非
4.3.4 函数对象适配器
函数适配器bind1st,bind2nd(参数绑定)
1. 函数适配器
1)创建函数对象类,继承binary_function<T1,T2,T3>
,声明()
重载,其中T1、T2是重载函数的2个参数,T3为重载函数的返回值类型
【注】定义public的()
重载时,必须由const修饰
2)应用函数适配器
for_each(v.begin(),v.end(),bind1st(函数对象类(),x));
for_each(v.begin(),v.end(),bind2nd(函数对象类(),x));
bind1st和bind2nd的区别
bind1st:将参数绑定为函数对象的第一个参数
bind2nd:将参数绑定为函数对象的第二个参数
bind1st和bind2nd 将二元函数对象转为一元函数对象
2. 取反适配器
1)定义unary_function<int,bool>
的函数对象类
2)应用
find_if(v.begin(),v.end(),函数对象类());
find_if(v.begin(),v.end(),not1(函数对象类()));
//动态给定条件
find_if(v.begin(),v.end(),nto1(bind2nd(greater<int>(),5)));
sort(v.begin(),v.end(),not2(less<int>()));
//匿名函数
for_each(v.begin(),v.end(),[](int val){cout<<cal<<" ";});
其中的 not1 对一元函数对象取反, not2 对二元函数对象取反。
3. 函数指针适配器
应用ptr_fun()
【注】仿函数的参数不能使用引用
4. 成员函数适配器
应用mem_fun_ref()
成员函数作为仿函数时,则通过容器成员调用它的仿函数
4.4 算法应用
算法中常用的功能涉及到比较、交换、查找、遍历、复制、修改、反转、排序、合并等
4.4.1 常用遍历算法
for_each(iterator beg,iterator end,_callback);
/*
遍历算法 遍历容器元素
@param beg 开始迭代器
@param end 结束迭代器
@param _callback 函数回调或者函数对象
@return 函数对象
*/
transfrom(iterator beg1,iterator end1,iterator beg2,_callback);
/*
@param beg1 源容器开始迭代器
@param end1 源容器结束迭代器
@param beg2 目标容器开始迭代器
@param _callback 回调函数或者函数对象
@return 返回目标容器迭代器
*/
将指定容器区间元素搬运到另一容器中
【注】不会给目标容器分配内存,所以需要我们提前分配好内存
4.4.2 常用查找算法
find(iterator beg,iterator end,value);
/*
find 算法 查找元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 查找的元素
@return 返回查找元素的位置
*/
find_if(iterator beg,iterator end,_callback);
/*
find_if 算法 条件查找
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param callback 回调函数或者谓词(返回 bool 类型的函数对象)
@return 返回查找元素的位置
*/
adjacent_find(iterator beg,iterator end,_callback);
/*
adjacent_find 算法 查找相邻重复元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param _callback 回调函数或者谓词(返回 bool 类型的函数对象)
@return 返回相邻元素的第一个位置的迭代器
*/
bool binary_search(iterator beg,iterator end,value);
/*
binary_search 算法 二分查找法
注意: 在无序序列中不可用
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 查找的元素
@return bool 查找返回 true 否则 false
*/
count(iterator beg,iterator end,value);
/*
count 算法 统计元素出现次数
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 回调函数或者谓词(返回 bool 类型的函数对象)
@return int 返回元素个数
*/
count_if(iterator beg,iterator end,value);
/*
count_if 算法 统计元素出现次数
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param callback 回调函数或者谓词(返回 bool 类型的函数对象)
@return int 返回元素个数
*/
4.4.3 常用排序算法
merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
/*
merge 算法 容器元素合并,并存储到另一容器中
注意:两个容器必须是有序的
@param beg1 容器 1 开始迭代器
@param end1 容器 1 结束迭代器
@param beg2 容器 2 开始迭代器
@param end2 容器 2 结束迭代器
@param dest 目标容器开始迭代器
*/
sort(iterator beg,iterator end,_callback);
/*
sort 算法 容器元素排序
@param beg 容器 1 开始迭代器
@param end 容器 1 结束迭代器
@param _callback 回调函数或者谓词(返回 bool 类型的函数对象)
*/
random_shuffle(iterator beg,iterator end);
/*
random_shuffle 算法 对指定范围内的元素随机调整次序
@param beg 容器开始迭代器
@param end 容器结束迭代器
*/
reverse(iterator beg,iterator end);
/*
reverse 算法 反转指定范围的元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
*/
4.4.4 常用拷贝和替换算法
copy(iterator beg,iterator end,iterator dest);
/*
copy 算法 将容器内指定范围的元素拷贝到另一容器中
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param dest 目标起始迭代器
*/
replace(iterator beg,iterator end,oldvalue,newvalue);
/*
replace 算法 将容器内指定范围的旧元素修改为新元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param oldvalue 旧元素
@param oldvalue 新元素
*/
replace_if(iterator beg,iterator end,_callback,newvalue);
/*
replace_if 算法 将容器内指定范围满足条件的元素替换为新元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param callback 函数回调或者谓词(返回 Bool 类型的函数对象)
@param oldvalue 新元素
*/
swap(container c1,container c2);
/*
swap 算法 互换两个容器的元素
@param c1 容器 1
@param c2 容器 2
*/
4.4.5 常用算术生成算法
引入
<numeric>
头文件
accumlate(iterator beg,iterator end,value);
/*
accumulate 算法 计算容器元素累计总和
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 累加值, 额外加的值,可以为0
@return 累加后的数值
*/
fill(iterator beg,iterator end,value);
/*
fill 算法 向容器中添加元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value t 填充元素
*/
4.4.6 常用集合算法
set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
/*
set_intersection 算法 求两个 set 集合的交集
注意:两个集合必须是有序序列
@param beg1 容器 1 开始迭代器
@param end1 容器 1 结束迭代器
@param beg2 容器 2 开始迭代器
@param end2 容器 2 结束迭代器
@param dest 目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
/*
set_union 算法 求两个 set 集合的并集
注意:两个集合必须是有序序列
@param beg1 容器 1 开始迭代器
@param end1 容器 1 结束迭代器
@param beg2 容器 2 开始迭代器
@param end2 容器 2 结束迭代器
@param dest 目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
/*
set_difference 算法 求两个 set 集合的差集
注意:两个集合必须是有序序列
@param beg1 容器 1 开始迭代器
@param end1 容器 1 结束迭代器
@param beg2 容器 2 开始迭代器
@param end2 容器 2 结束迭代器
@param dest 目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
标签:容器,end,迭代,iterator,stl,元素,param,标准
From: https://www.cnblogs.com/dijun666/p/17859410.html