0
点赞
收藏
分享

微信扫一扫

STL的泛型算法使用详解(1)

Jonescy 2022-01-14 阅读 32

目录

一、什么是泛型算法

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是在迭代器遍历时内部传参的。

  学习过程中要求能看懂理解网页中的理论知识,能看懂提供的源码和测试代码,自己会用。

注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

举报

相关推荐

0 条评论