目录
一、什么是泛型算法
1、从容器说起
(1)容器是数据结构,是对数据的封装
(2)各容器都提供了少量处理元素操作的方法,譬如sort,但没有提供更多
(3)同样的操作譬如sort,在不同容器中底层处理肯定会不同
(4)泛型算法是独立于容器类的一些操作方法,可以用于多种容器,所以叫“泛型”算法
(5)泛型算法实际上是更高层次的抽象,所以设计和实现的难度很大,这也是STL的核心技术
2、泛型算法使用基础
(1)泛型算法学习参考:https://zh.cppreference.com/w/cpp/algorithm
泛型算法在元素范围上操作,即其操作的是元素的值。注意范围定义为 [first, last) ,其中 last 指代要查询或修改的最后元素的后一个元素。
(2)泛型算法所在头文件
二、泛型算法使用实战
1、泛型算法和容器迭代器的适配
(1)算法中对迭代器有要求
(2)容器中包含的迭代器有特性
(3)2者兼容才可结合使用,否则不可使用
template< class RandomIt >
void sort( RandomIt first, RandomIt last );(C++20 前)
类型要求:
-RandomIt 必须满足值可交换 (ValueSwappable) 和 老式随机访问迭代器 (LegacyRandomAccessIterator) 的要求。
-解引用 RandomIt 结果的类型必须满足可移动赋值 (MoveAssignable) 和可移动构造 (MoveConstructible) 的要求。
2、泛型算法使用举例
下面的这个sort函数并非这几个容器自带的算法,而是泛型算法sort:
https://zh.cppreference.com/w/cpp/algorithm/sort
(1)使用sort为array默认排序
(2)使用sort为list默认排序
(3)使用sort为list由大到小排序
#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
template <typename T>
void print(const T& a)
{
for(auto iter = a.begin(); iter != a.end();)
{
cout << *iter++ << ' ';
}
cout << endl;
}
int main(int argc, char *argv[])
{
array<int, 5> a1{2, 1, 7, 6, 10};
vector<int> a2{1, 10, 0, 7, 9};
list<int> a3{5, 6, 7, 2, 3};
sort(a1.begin(), a1.end());//默认从小到大的顺序排列
sort(a2.begin(), a2.end(), greater<int>());//按从大到小的顺序排列
a3.sort();//list的迭代器不支持随机访问,故而不可使用泛型算法,可使用其自带的sort方法
print(a1);
print(a2);
print(a3);
return 0;
}
三、谓词predicate和函数对象引入
1、何为谓词
(1)谓词就是可以做谓语的词,就是“动词、动作”性质语义的词
(2)C/C++中的函数function就是典型的谓词语义
(3)C++ STL中的谓词类似这样:bool func(T& a); 或者 bool func(T&a, T& b);
(4)常见的谓词:函数,函数指针,lambda表达式,函数对象,库定义的函数对象
2、函数对象引入
(1)函数对象 function object, 也叫仿函数 functor
(2)函数对象在语法上不是函数,而是个类
sort(a2.begin(), a2.end(), greater<int>());
greater就是个函数对象。
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
(3)函数对象在调用形式上看起来像个函数
3、函数对象案例实践
(1)写一个函数,判断传参是否大于0
(2)用函数对象实现,对比、分析、体会
#include <iostream>
using namespace std;
template <typename T>
bool IsGreater_function(T a)
{
return (a>0);
}
template <typename U>
class IsGreater_class
{
public:
//我们通过()的运算符重载函数实现同样的功能
bool operator()(U a)
{
return (a>0);
}
};
int main(int argc, char *argv[])
{
//使用函数实现
bool b = IsGreater_function(5);
cout << boolalpha << b << endl;
b = IsGreater_function(-5);
cout << boolalpha << b << endl;
//使用函数对象实现
IsGreater_class<int> is_greater;
b = is_greater(6);
cout << boolalpha << b << endl;
b = is_greater(-6);
cout << boolalpha << b << endl;
return 0;
}
四、函数对象的一些细节
1、STL中定义的函数对象的分析
由于那个C++手册的网页加载比较慢,有些链接打不开,我将这个手册下载了下来,和网页版
相比的缺陷在于不会更新新的内容,我把我下载的分享给大家,链接如下:
链接:https://pan.baidu.com/s/1tyRc8B95ifXkkE3Vw9ZUoQ
提取码:wy6n
--来自百度网盘超级会员V5的分享
2、自定义函数对象用于STL算法库
#include <iostream>
#include <array>
#include <algorithm>
using namespace std;
//函数声明
template <typename T> void print(const T& a);
//自定义的函数对象,通过字符串的个数进行比较
class mygreater
{
public:
bool operator()(const string s1, const string s2)
{
return (s1.size() > s2.size());
}
};
template <typename T>
void print(const T& a)
{
for(auto iter = a.begin(); iter != a.end();)
{
cout << *iter++ << ' ';
}
cout << endl;
}
int main(int argc, char *argv[])
{
array<string, 3> a = {"linux", "android", "harmonyos"};
sort(a.begin(), a.end());//默认是根据字符串首字母的字典序进行比较的,排序按照从小到大
print(a);
sort(a.begin(), a.end(), greater<string>());//使用泛型算法提供的函数对象greater
print(a);
sort(a.begin(), a.end(), mygreater());//使用自定义的函数对象mygreater
print(a);
return 0;
}
3、函数对象的优势
(1)函数对象可以使用template技术实现多类型支持,这比函数的重载技术更有优势
(2)函数对象可以有自己的状态。我们可以在类中定义状态变量(类私有成员变量),这样一个函数对象在多次的调用中可以共享这个状态。比如我们可以用这个状态表示函数被调用的次数。
参考学习:https://www.cnblogs.com/gis-user/p/5086218.html
五、STL典型泛型算法解读
我们随机打开一个来分析学习:https://zh.cppreference.com/w/cpp/algorithm/all_any_none_of
template< class InputIt, class UnaryPredicate >
bool all_of( InputIt first, InputIt last, UnaryPredicate p );
UnaryPredicate:一元谓词,表示该谓词只接收一个参数
struct Divisible By
{
const int d;
DivisibleBy(int n) : d(n) {}
bool operator()(int n) const { return n % d == 0; }
};
if (std::any_of(v.cbegin(), v.cend(), DivisibleBy(7))) {
std::cout << "At least one number is divisible by 7\n";
}
std::any_of(v.cbegin(), v.cend(), DivisibleBy(7))中的7对应DivisibleBy成员变量
d,这里的DivisibleBy(7)既与构造函数有关(进行初始化),也与运算符重载函数有关(作
为函数对象),DivisibleBy(int n)中的n是在迭代器遍历时内部传参的。
学习过程中要求能看懂理解网页中的理论知识,能看懂提供的源码和测试代码,自己会用。
注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。