-
C++中除了面向对象的编程思想外,还有另一种就是泛型编程
-
主要用到的技术就是模板
模板机制的分类:
-
函数模板
-
类模板
函数模板
作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体定制,用虚拟的类型来表示
关键字:template
语法:
template <typename T> // template <class T>
函数声明或者定义
-
template:声明创建模板
-
typename:表明后面的符号是一种数据类型,也可以用class代替
-
T:通用的数据类型,名称可以替换,通常为大写字母
意义:将类型参数化,提高代码通用性。
使用方法:
-
自动类型推导
-
显示指定类型
注意事项:
-
自动类型推导,无论有几个 T,其必须要推导出一致的数据类型,才可以使用
-
函数模板必须要确定 T 的数据类型,才可以使用
与普通函数的区别:
-
普通函数调用时可以发生自动类型转换,也就是隐式转换
-
函数模板只有在调用显示指定类型才会发生隐式转换,用自动类型推导就不会
示例:
调用规则
-
如果普通函数和函数模板都实现了,优先使用普通函数
-
可以通过空模板参数列表强制调用函数模板
-
函数模板可以以发生函数重载
-
如果函数模板可以更好的匹配,那肯定是优先使用函数模板啦
示例:
函数模板的局限性
一些特定的数据类型,函数模板就处理不了
示例:
-
为了解决这些问题,C++提供了模板的重载,为特定的数据类型提供具体化的模板
-
利用具体化的模板,可以解决类型自定义的通用性
-
但是模板其实不用去写,可以在STL中调用系统提供的模板
语法:
template<> 返回值类型 函数名(参数1,参数2,···)
示例:
类模板
作用:建立一个通用的类,但是类中的成员变量类型未知,用一个虚拟的类型来表示
语法:
template<typename T> 类
-
template:声明创建一个模板
-
typename:表明后面的 T 是一种数据类型,typename 也可以用class替代
-
T:通用的数据类型
与函数模板的区别:
-
类模板在使用的时候,没有自动类型推导的方式
-
类模板在模板参数列表中可以有默认参数类型
示例:
类模板成员函数创建时机
-
普通类中的成员函数,在一可以就可以创建
-
类模板中的成员函数,在函数调用时才创建
示例:
类模板对象做函数参数
作用:用模板实例出的对象来当做函数参数传入
传入方式:
-
指定传入的类型:直接显示对象的数据类型
-
参数模板化:将对象中的参数变为模板进行传递
-
整个类模板化:将整个对象进行模板化,传递下去
一般而言第一种传递方式比较常见;
第二种和第三种,其实有点像函数模板和类模板结合使用;
使用函数 typeid(temp) 可获知一个参数 temp 的类型;
示例:
类模板与继承
当类模板碰到继承时:
-
当子类继承的父类是一个类模板时,子类在声明时要指定父类 T 的类型
-
如果不指定,编译器就无法给子类分配内存
-
要想灵活指定父类中 T 的类型,子类也必须为类模板
语法:
template<class T1 , class T2>
class 子类:继承方式 父类<T2>
{
//T1 通用类型留给子类自己用
}
示例:
类模板成员函数的类外实现
类外实现的方法:
-
类内声明,类外写函数定义
-
类外写函数时,作用域需要加上模板参数列表
语法:
template<class T>
返回值类型 作用域<T>::成员函数()
//作用域要加上模板参数列表<T>,不管传参和函数内部有没有用到 T ,<T> 都要写出来
{
}
示例:
分文件编写
在使用类模板过程中,进行分文件编写会出现一些问题:
一般的分文件编写:
.h:写类声明和成员函数定义
.cpp:写类中成员函数的实现
x.cpp:写主函数和应用功能
原因:类模板的成员函数在主函数调用的时候才创建,所以会导致预处理时链接不到
解决办法(建议用第二种):
-
直接包含.cpp源文件(将x.cpp中的#include”.h”>改成#include”.cpp”,而.cpp中也有包含头文件,这样改,就是在跑主函数之前,将声明和实现都跑了一遍)
-
将声明和实现写在同一个文件中,后缀名改成.hpp(.hpp是约定俗成的名字,不是强制的,但大家都这么写)
两种方法,目的都是在跑主函数之前,将声明和实现都先跑了一遍。
类模板与友元
类模板和友元配合成的全局函数的类内实现和类外实现:
-
类内实现:直接在类内声明友元即可(建议这个,比较简单)
-
类外实现:需要提前让编译器知道全局函数的存在(有点复杂,不建议)
示例:
STL
-
之所以有C++的面向对象和泛型编程,目的就是提升可重复性
-
STL的诞生,就是为了建立一套数据结构和算法的标准,提高可重复利用性
STL基本概念
-
STL,即标准模板库
-
从广义上分为:容器、算法、迭代器
-
从细分上分为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
-
容器和算法通过迭代器进行无缝连接
-
STL所有的代码都运用到了函数模板和类模板
STL六大组件
六大组件就是上线说到的:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
-
容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
-
算法:各种常用算法,如sort、find、copy、for_each等
-
迭代器:容器和算法之间的链接器
-
仿函数:类似函数,可作为算法的某种策略
-
适配器:一种用来修饰容器或者仿函数或者迭代器的东西
-
空间配置器:负责空间的分配和管理
容器
作用:存放数据,将广泛使用的数据结构给实现出来
常用的数据结构:数组、链表、树、栈、队列、集合、映射表等
容器的分类:
-
序列式容器:强调值的位置,每个元素都有固定的位置
-
关联式容器:二叉树结构,元素之间没有严格的物理上的顺序关系
算法
作用:问题的解法,即用有限的步骤,解决逻辑上的难题
算法的分类:
-
质变算法:运算中更改区间内元素的内容,如拷贝、替换、删除等
-
非质变算法:运算中不会更改区间内元素的内容,如查找、计数、遍历、寻值等
迭代器
作用:容器和算法之间的链接器,即就是提供一种方法,既能依序寻找容器内某个元素,又不暴露容器内部的表示方法
-
每个容器都有自己专属的迭代器
-
迭代器的使用方法类似于指针
-
最常用的是双向迭代器和随机访问迭代器
迭代器的种类:
种类 |
功能 |
支持运算 |
---|---|---|
输入迭代器 |
对数据的只读访问 |
只读,支持++、==、!= |
输出迭代器 |
对数据的只写访问 |
只写,支持++ |
前向迭代器 |
对数据的向前操作 |
读写,支持++、==、!= |
双向迭代器 |
对数据的向前和向后操作 |
读写,支持++、– |
随机访问迭代器 |
访问任何数据,功能最强 |
读写,支持++、–、[n]、-n、<、<=、>、>= |
String容器
基本概念
-
string是C++风格的字符串,但是本质上是一个类
-
String是内部封装了char类型,管理string这个类,用的是char*的容器
-
包含头文件#include<string>
特点
-
string内部封装了很多成员函数方法,比如find查找,copy复制,delete删除,replace替换,insert插入等等
-
string管理char*所分配出来的内存,不用担心复制越界内存和取值越界,也不用担心溢出或者碎片,这些由容器内部统一管理
string构造函数
-
string();无参构造,主要用来创建一个空字符串
-
string(const char* s); 用来初始化一个字符串
-
string(const string& str); 用一个string对象来初始化另一个string对象
-
string(int n,char c); 初始化n个字符c
-
以上构造函数没有什么可比性,根据实际情况灵活运用即可
示例:
赋值操作
作用:给string字符串赋值
函数原型:
-
string& operator= ( const char *s ) ; 将char *类型的字符串赋值给当前字符串
-
string& operator= ( const string &s ) ; 将string类型字符串赋值给当前字符串
-
string& operator= ( char c ) ; 将字符c赋值给当前字符串
-
string& assign ( const char*s ) ; 将char *类型字符串赋值给当前字符串
-
string& assign ( const char *s, int n ) ; 将char *类型的字符串的前n个字符赋值给当前字符串
-
string& assign ( const string &s ) ; 将const类型的字符串赋值给当前字符串
-
string& assign ( int n,char s ) ; 将n个字符s赋值给当前字符串
示例:
字符串拼接
作用:就是在字符串末尾再拼接一段字符串
函数原型:
-
string& operator+ ( const char *c ) ; //将char*类型的字符串拼接到当前字符串末尾
-
string& operator+ ( const char c ) ; //将字符c拼接到当前字符串末尾
-
string& operator+ ( const string& str ) ; //将string类型的字符串拼接到当前字符串末尾
-
string& append ( const char *s ) ; //将char*类型的字符串拼接到当前字符串末尾
-
string& append ( const char *s , int n ); //将char*类型字符串的前n个字符拼接到当前字符串末尾
-
string& append ( const string &s ) ; // 将string类型额字符串拼接到当前字符串末尾
-
string& append ( const string &s, int pos , int n ); //将string类型的字符串从pos个开始的n个字符拼接到当前字符串末尾
示例:
字符串的查找和替换
作用:
-
查找:查找指定的字符串是否存在
-
替换:在指定的位置替换字符串
函数原型:
-
int find ( const string& str , int pos = 0 ) const ; //查找str第一次出现的位置,从pos开始查找
-
int find ( const char* s , int pos = 0 ) const ; //查找 s 第一次出现的位置,从pos开始查找
-
int find ( const char* s ,int pos , int n ) const ; //查找s的前n个字符第一次出现的位置,从pos开始查找
-
int find ( const char c , int pos = 0 ) const ; //查找字符串 c 第一次出现的位置,从pos开始查找
-
int rfind ( const string& str , int pos = npos ) const; //查找str最后一次的位置,从pos开始查找
-
int rfind ( const char* s , int pos = npos ) const ; //查找 s 最后一次出现的位置,从pos开始查找
-
int rfind ( const char* s , int pos , int n) const; //查找 s 的前n个字符最后一次的位置,从pos开始查找
-
int rfind ( const char s , int pos = 0 ) const ; //查找字符 s 最后一次出现的位置,从pos开始查找
-
string& replace ( int pos , int n , const string& str) ; //从pos开始的n个字符,替换成字符串str
-
string& replace ( int pos , int n , const char* s); //从pos开始的n个字符,替换成字符串 s
特点:
-
find是从左往右找,rfind是从右往左找,找到了返回下标位置,找不到返回 -1
-
replace会把替换字符串全部替换进去,不管你指定多少个字符
示例:
字符串比较
作用:字符串之间的比较
特点:
-
以其中的字符的ASCII值得方式进行比较
-
> 返回 1
-
< 返回 -1
-
= 返回 0
-
主要是用来比较两个字符串是否一样,比较大小没有什么意义
函数原型:
-
int compare ( const string& s) const ; //与string类型的字符串进行比较
-
int compare ( const char s ) const ; //与char*类型的字符串进行比较
示例:
字符存取
对单个字符进行存取有两种方法:
-
通过 [ ] 方式进行存取
-
通过at函数方式进行存取
函数原型:
-
char& operator[](int n) ; //通过[]方式存取
-
char& at(int n); //通过at方式存取
示例:
字符的插入和删除
作用:对字符串中进行插入和删除字符的操作
函数原型:
-
string& insert ( int pos , const char* s ) ; //从pos位置开始插入字符
-
string& insert ( int pos , const string& s); //从pos位置开始插入字符
-
string& insert ( int pos , int n, char s); //从pos位置开始,插入n个字符s
-
string& erase( int pos , int n = npos ) ; //从pos位置开始,删除n个字符
-
参数pos的位置,都是从下标0开始的
示例:
字符串子串
作用:从字符串中截取想要的子串
函数原型:
-
string substr( int pos , int n = npos) const ; //从pos位置开始,截取n个字符
示例:
Vector容器
基本概念
-
vector数据结构与数组非常相似,但是只能进行尾部的插入和删除,所以也被称为单端数组
-
vector容器的迭代器是支持随机访问的迭代器,功能最强大的迭代器之一
-
包含头文件 #include<vector>
与普通数组的区别
-
数组是静态空间
-
vector可以动态扩展
-
动态扩展并不是在原空间上续接新空间,而是寻找更大的空间,将原数据拷贝过去,在新空间后续接空间,释放源空间
构造函数
作用:创建vector容器
函数原型:
-
vector <T> v ; //默认构造函数,T 是数据类型 , v是容器名字
-
vector ( v.begin() , v.end() ) ; //将v.[ begin() , end() ) 区间中的元素拷贝给本身
-
vector ( n , a ) ; //构造函数将 n 个 a 数值拷贝给本身
-
vector ( const vector &ver ) ; //拷贝构造函数
示例:
vector赋值
作用:给vector容器进行赋值操作
函数原型:
-
vector& operator= ( const vector &vst ) ; //重载方式进行赋值
-
assign ( begin , end ) ; //将 [ begin , end )区间内的数值赋值给本身
-
assign (n , A) ; //将n个字符A赋值给本身
示例:
容量和大小
作用:获取vector容器的容量和大小参数
函数原型:
-
empty ( ) ; //判断是否为空
-
capacity ( ) ; //获取容器的容量
-
size ( ) ; //获取元素个数
-
resize ( int num ) ; //重新指定容器长度,容器变长,超出的用0表示;容器变短,超出的被删除
-
resize ( int num,int elem ) ; //重新指定容器元素,容器变长,超出的用elem表示,变短则超出的被删除
示例:
插入和删除
作用:在容器中插入元素和删除元素
函数原型:
-
push_back ( ) ; //尾部插入
-
pop_back ( ) ; //删除尾部最后一个元素
-
insert ( const_iterator pos , elem ) ; //插入元素elem,第一个参数是迭代器指向位置
-
insert (const_iterator pos , int num , elem ) ; //插入num个elem数值,第一个参数是迭代器指向位置
-
erase ( const_iterator pos ); //删除元素,第一个参数是迭代器指向位置
-
erase ( const_iterator begin , const_iterator end ); //删除从头到尾的元素
-
clear ( ); //删除容器内全部元素
示例:
数据存取
作用:获取容器中的数据
函数原型:
-
at ( int idx ) ; //返回索引下的元素
-
operator[ ] ; //重载方式返回索引下的元素
-
front ( ) ; //获取容器第一个元素
-
back ( ) ; //获取容器中最后一个元素
示例:
容器互换
作用1:实现两个容器之间的元素互换
作用2:用于收缩容器容量(原理就是创建一个小容量的容器与本身的大容量容器互换)
函数原型:
-
swap( vec ) ; //将容器vec中的元素与本身互换
示例:
预留内存
作用:在一开始就开辟出足够的空间,减少动态扩展的次数
原理:因为一旦容器输入数据量较大的时候,编译器会根据内存实际情况时不时进行动态扩展,动态扩展次数一旦多了,操作时间就长了,所以最好是在一开始就预留足够多的空间,将动态扩展次数维持为1次。
函数原型:
-
reserve ( int len ) ; //预留len个元素长度,预留位置不初始化,也不可以访问
示例:
vector排序
-
作用:利用算法对容器内元素进行排序
-
需要包含算法头文件 #include <algorithm>
-
对于随时访问的迭代器都可以使用该排序函数
函数原型:
-
sort( iterator begin , iterator end ) ; // 对begin和end之间的元素进行从小到大排序
示例:
deque容器
-
与数组类似,可以进行头部和尾部的插入和删除,因为也被称为双端容器
-
deque容器的迭代器也是支持随机访问的
-
头文件#include <deque>
deque与vector的区别:
-
vector容器只能尾部插入和删除,因为对于头部操作效率很低,数据量越大越低
-
deque容器头部的插入和删除速度比vector快多了
-
vector和deque内部结构不同,导致元素的速度vector会更快,导致deque没有容量限制,可以无限扩张
-
二者的迭代器都支持随机访问,是最强大的迭代器之一
内部工作原理
-
deque内部有一个中控器,用于记录每段缓冲区的地址,缓冲区中放置真实数据
-
中控器+缓冲区,使得deque看起来像是一块连续的内存
构造函数
作用:创建deque容器
函数原型:
-
deque<T> d ; //默认构造函数,T表示数据类型
-
deque( begin , end ) ; //将容器[ begin , end )区间内的数据拷贝给本身
-
deque( count , elem ) ; //将count个elem数值拷贝给本身
-
deque ( const deque& d ) ; //拷贝构造函数,将容器数据整个拷贝给本身
示例:
赋值操作
作用:给deque容器进行赋值
函数原型:
-
deque& operator=( const deque& d) ; //重载方式进行赋值
-
assign ( begin , end ); //将[ begin , end )区间内的数据赋值给容器
-
assign ( count , elem ); //将count个elem数值赋值给容器
示例:
获取大小,没有容量限制
-
作用:获取deque内部的元素长度
-
由于中控器+缓冲区的内部结构,导致deque是没有容量限制的,理论上内存可以无限扩张,所以没有获取容量的函数
函数原型:
-
empty( ) ; // 判断容器是否为空
-
size( ) ; //获取元素个数
-
resize ( num ) ; //重新指定容器长度,若容器变长,以默认值0填充新位置;若长度变短,超出部分删除
-
resize ( num , elem ) ; //重新指定容器长度,若容器变长,以elem填充新位置,若变短,超出部分删除
示例:
插入和删除
作用:在容器两端进行插入和删除新数据
函数原型:
-
push_back( elem); // 尾部插入数据elem
-
push_front( elem); //头部插入数据elem
-
pop_back( ); //删除最后一个元素
-
pop_front( ); //删除第一个元素
-
insert( pos ,elem); //在迭代器位置pos插入数据elem
-
insert( pos , n, elem ); //在迭代器位置pos插入n个elem数据
-
insert( pos, begin, end ); //在迭代器位置pos插入[begin , end)区间内的数据
-
erase( pos); //删除迭代器位置pos的数据
-
erase( begin , end ); //删除从begin到end之间的数据
-
clear(); //删除容器内所有元素
示例:
数据存取
作用:对deque容器中的数据进行存取
函数原型:
-
operator[ ] ; //重载方式返回索引idx的数据
-
at( int dix ) ; //at()返回索引idx的数据
-
front( ) ; //返回第一个元素
-
at( ) ; //at()返回索引idx的数据
示例:
deque排序
-
作用:利用算法对容器内元素进行排序
-
需要包含算法头文件 #include <algorithm>
-
对于随时访问的迭代器都可以使用该排序函数,包括vector容器
函数原型:
-
sort( iterator begin , iterator end ) ; // 对begin和end之间的元素进行从小到大排序
示例:
stack容器
-
stack容器是一种前进后出的数据结构,又称为栈容器
-
该容器只有顶端的元素才可以给外界获取
-
该容器不允许有遍历行为
-
包含头文件 #include <stack>
构造函数
函数原型:
-
stack<T> stk ; //默认构造函数
-
stack( const stack &stk ) ; // 拷贝构造函数
赋值操作
函数原型:
-
stack& operator=( const stack& stk ) ; //重载方式进行赋值
数据存取
函数原型:
-
push( elem ) ; //向栈顶添加元素
-
pop( ) ; //删除栈顶第一个元素
-
top( ) ; //返回栈顶元素
大小获取操作
函数原型:
-
empty( ) ; //判断容器是否为空
-
size( ) ; // 返回容器的大小
示例:
queue容器
-
queue容器是一种先进先出的数据结构,又称为队列容器
-
允许从一段添加元素,另一端删除元素
-
只有队尾和队头可以被外界使用,因此不允许有遍历行为
-
包含头文件 #include<queue>
构造函数
函数原型:
-
queue<T> que ; //默认构造函数
-
queue( const queue& que ) ; //拷贝构造函数
赋值操作
函数原型:
-
queue& operator=( const queue& que ) ; //重载方式赋值
数据存取
函数原型:
-
push( elem ) ; //往队尾添加元素
-
pop( ) ; //删除队头的元素
-
back( ) ; //返回最后一个元素
-
front( ) ; //返回第一个元素
大小获取
函数原型:
-
empty( ) ; //判断队列是否为空
-
size( ) ; // 判断队列的大小
示例;
list容器
-
list容器是一种双向循环的数据结构,又称为链式容器
-
内部结构并不是连续的内存,迭代器不能随访问,只能前移和后移,属于双向迭代器
-
包含头文件 #include<list>
-
动态存储分配,不会存在内存浪费和溢出的问题
-
随机访问,插入和删除非常方便
-
占用空间比较大,遍历的消耗非常大
构造函数
作用:创建一个list容器
函数原型:
-
list<T> lst ; //默认构造函数
-
list( begin , end ) ; //将区间 [ begin ,end)之间的元素拷贝给本身
-
list( n , elem ) ; // 将 n个elem拷贝给本身
-
list( const list &lst) ; // 拷贝构造函数
赋值与交换
作用:给list容器赋值,以及交换list容器
函数原型:
-
list& operator=( const list& lst ) ; // 重载方式进行赋值
-
assign( begin() , end() ) ; //将区间[ begin , end)之间的元素赋值给本身
-
assign( n , elem ) ; //将 n 个 elem赋值给本身
-
swap( lst ) ; // 将容器 与 本身的元素互换
示例:
大小操作
作用:获取容器list的长度
函数原型:
-
size( ) ; //返回元素个数
-
empty( ) ; // 判断是否为空
-
resize( num ) ; //重新指定容器长度,容器变长,以默认值0填充,容器变短,超出的元素被删除
-
resize( num , elem ) ; //重新指定容器长度,容器变长,以数值elem填充,容器变短,超出的元素被删除
示例:
插入和删除
作用:对list容器进行元素插入和删除
函数原型:
-
push_back( elem ); //尾部插入数据
-
push_front( elem ); //头部插入数据
-
pop_back(); //删除最后一个元素
-
pop_front(); //删除第一个元素
-
insert( pos, elem ); //在迭代器pos位置插入数值elem
-
insert( pos , n , elem ); //在迭代器pos位置插入n个elem
-
insert( pos , begin , end ); //在迭代器pos位置插入从begin到end之间的元素
-
erase( pos ); //删除迭代器pos位置的元素
-
erase( begin , end ); //删除迭代器[ begin , end ) 之间的元素
-
remove( elem ); //删除容器中与elem相同的所有元素
示例;
数据存取
-
存取方式不是[]获取,也不是at()获取
-
因为list容器本质是链表,既不是连续的内存,迭代器也不支持随机访问
函数原型:
-
front(); // 返回第一个元素
-
back(); // 返回最后一个元素
示例:
反转和排序规则
作用:将容器中的元素反转,以及对元素进行排序
函数原型:
-
reverse(); //容器元素进行反转
-
sort(); //容器元素进行从小到大排序,没有迭代器参数,所以这个是内部成员函数,不是标准算法
-
//对于定义数据类型,必须要指定排序规则(高级排序),否则编译器是不知道怎么排的
示例:
set/multiset容器
-
在set/multiset容器中,所有的元素都会在插入时自动排序,默认从小到大
-
两者属于关联式容器,内部结构是用二叉树实现的
-
两者的头文件都是 #include<set>
set和multiset的区别:
-
set容器不允许有重复的元素出现,即使强制插入重复元素,插入也会失败
-
multiset容器可以有重复的元素出现
set的构造函数
作用:创建set容器
函数原型:
-
set<T> st ; // 默认构造函数
-
set(const set& st) ; //拷贝构造函数
set的赋值函数
函数原型:
-
set& operator( const set& st); // 函数重载实现赋值操作
set的插入和删除
函数原型:
-
insert( elem ) ; // 插入只有insert()方式,不能指定位置,因为会自动排序
-
erase(pos) ; // 删除迭代器pos位置的元素
-
erase ( elem ); //删除数值为elem的元素
-
erase( begin , end ); //删除[ begin,end)之间的元素
-
clear(); //清除所有元素
set的大小和交换
-
作用:统计容器的大小以及交换set容器
-
没有重新指定容器大小的操作,因为会有默认值填充位置,而set容器中不能出现重复的元素
函数原型:
-
size( ); //返回容器中元素个数
-
empty( ); //判断容器是否为空
-
swap( ); // 交换两个集合容器中的数据
set的查找和统计
作用:对set容器进行数据查找以及数据统计
函数原型:
-
find( key ); //查找一个元素是否存在,存在则返回元素迭代器位置,不存在返回set.end()
-
cout( key ); //统计key的元素个数,对于set而言不是0就是1,因为不允许有重复元素
示例:
set和multiset的区别
-
set不可以插入重复数据,而multiset可以
-
set插入数据会返回插入结果,同时也是这样检测是不是重复元素
-
multiset插入数据不会检测,因此可以插入重复数据
set.insert()的函数原型:
-
_Pairib insert( value_type&& _Val) ;
-
using _Pairib = pair<inerator , bool>; //set.insert()的返回值类型是对组,返回迭代器位置和插入结果
multiset.insert()的函数原型:
-
iterator insert(value_type&& _Val); //multiset.insert()返回值类型只有迭代器位置,没有检测结果
示例:
pair对组创建
作用:成对出现的数据,可以返回两个数据
创建方法的函数原型:
-
pair<type , type> p (value1, value2 );
-
pair<type , type> p = make_pair( value1, value2 );
示例:
set容器排序
-
作用:set容器排序是默从小到大,但是可以可以通过仿函数来改变排序规则
-
要在插入数据之前就指定排序规则
-
如果是自定义数据类型,一定要指定排序规则
示例1:指定排序规则——内置数据类型
示例2:指定排序规则——自定义数据类型
map/multimap容器
-
map/multimap都是关联式容器,内部结构是二叉树
-
map容器中的元素全是pair对组
-
pair中第一个元素为key(键值,起到索引作用),第二个元素为value(实值)
-
所有的元素都会根据元素的key键值自动排序,默认从小到大
-
可以根据键值快速找到value值,高效率高性能
-
map/multimap两者的头文件都是#include<map>
map和multimap的区别:
-
map容器中不允许有重复的key值元素
-
multimap容器中允许有重复的key值元素
map的构造函数
函数原型;
-
map<T1 , T2> mp ; // 默认构造函数
-
map<const map& mp> ; // 拷贝构造函数
map的赋值函数
函数原型:
-
map& operator=( const map& mp) ; // 重载等号操作符进行赋值操作
map的大小和交换
函数原型:
-
size(); // 返回元素个数
-
empty(); // 判断容器是否为空
-
swap(); // 交换两个map容器
map的插入和删除
函数原型:
-
insert(elem); // 插入元素,地址没法自定义,因为会自动排序
-
erase(key); // 删除key值的元素
-
erase(pos); // 删除迭代器pos位置的元素
-
erase(begin , end); // 删除区间为[begin,end)之间的元素
-
clear(); // 清除容器中所有元素
map的查找和统计
函数原型:
-
find( key ); //查找元素是否存在,存在则返回元素迭代器,不存在则返回map.end() ;
-
count( key ); //返回键值为key的元素的个数,对于map,不是0就是1,因为不允许出现重复元素
示例:
map容器排序
-
作用:利用仿函数,改变排序规则
-
排序规则一定要在插入数据前指定
示例:
函数对象/仿函数
-
本质上是一个类,重载函数调用运算符的类,其创建的对象也就被称为函数对象
-
因为函数对象在发生重载时,就像函数调用一样,所以也叫做仿函数
-
说白了就是一个对象,一个可以当函数用的对象
特点:
-
函数对象在使用时跟函数差不多,可以有参数,也可以有返回值
-
函数对象可以有自己的状态,函数没有
-
函数对象可以作为参数传递
示例:
谓词
-
返回bool类型的仿函数,就称为谓词
-
如果operator()接受一个参数,就是一元谓词
-
如果operator()接受两个参数,就是二元谓词
一元谓词
-
如果operator()接受一个参数,就是一元谓词
示例:
二元谓词
-
如果operator()接受两个参数,就是二元谓词
示例:
内建函数对象
-
STL内建了一些已经封装好了的函数对象
-
这些对象是由仿函数所产生的,用法和一般对象一样
-
需要加入头文件#include<functional>
-
内建函数对象是已经写好的标准算法入口,愿意你就用,不愿意就自己写一个
分类:
-
算术仿函数
-
关系仿函数
-
逻辑仿函数
算术仿函数
作用:实现四则运算
函数原型:
-
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> //取反仿函数 ,一元运算
示例:
关系仿函数
作用:实现关系对比
函数原型:
-
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<calss T> bool less<T> //小于
-
template<class T> bool less_equal<T> //小于等于
示例:不用自己写的仿函数,用内建函数对象来改变排序规则
逻辑仿函数
作用:实现逻辑运算
函数原型:
-
template<class T> bool logical_and<T> // 逻辑与
-
template<class T> bool logical_or<T> //逻辑或
-
template<class T> bool logical_not<T> //逻辑非
示例:用逻辑非操作将容器v1的元素取反并搬运到容器v2中
先更新到这儿吧,需要后面在补充。
希望以上内容可以帮助到大家。
祝各位生活愉快。