联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

C++标准库学习之 函数对象

作者:访客发布时间:2023-12-19分类:程序开发学习浏览:76


导读:在C++中存在一种比较怪异的用法,那就是使用一个对象当做函数,先看下面一个简单的例子#include<iostream>#include<vector&...

在C++ 中存在一种比较怪异的用法,那就是使用一个对象当做函数,先看下面一个简单的例子

#include <iostream>
#include <vector>
#include <algorithm>

class Person{

public:
    void operator()(int element) const{
        std::cout<<element<<std::endl;
    }
};

int main(){

    Person p;

    std::vector<int> tsm={1,2,3,4,5,6,7,8,9};

    std::for_each(tsm.cbegin(),tsm.cend(),p);
    
  return 0;
}

这种用法就是在遍历过程中 将p 这个对象传入 for_earch 算法中,for_each 会调用 p 的 operator ()(int element) const; 方法,这个举动看起来有点怪异, 至于为什么要这么干, 原始是 Person 既然是一个对象,这个对象就可以拥有其他属性,如果这个属性是根据上下文关系计算出来的,我们在调用 他的 operator () 方法时,就能非常简单的使用这个他所拥有的这些属性,

在不使用函数对象时,如果我们需要遍历 for_earch ,打印他的信息该如何操作呢,看下面这个例子,我们使用 Lambda 表达式来完成这简单的操作

#include <iostream>
#include <vector>
#include <algorithm>

class Person{

public:
    void operator()(int element) const{
        std::cout<<element<<std::endl;
    }
};

int main(){
    std::vector<int> tsm={1,2,3,4,5,6,7,8,9};

    std::for_each(tsm.cbegin(),tsm.cend(),[](const int element){
        std::cout<<element<<std::endl;
    });
  return 0;
}

现在如果我们需要给这个 element 增加一个固定的 数值,并将这个算法修改为一个模板函数该如何写呢

template<int value>
void addInt(int element){
    std::cout<<(element+value)<<std::endl;
}


int main(){
    std::vector<int> tsm={1,2,3,4,5,6,7,8,9};

    std::for_each(tsm.cbegin(),tsm.cend(),addInt<10>);
  return 0;
}

但是现在我们又遇到了另一种情况, 那就是这个10 是计算出来的结果, 但是由于 template< int value > 这种写法是将 value 变成了静态变量, 而我们计算出来的值又不是就使得这种模板的方式没有办法继续使用了,此时就轮到了 函数对象出场来拯救世界了,

#include <iostream>
#include <vector>
#include <algorithm>

class Person{

private:
    int value=0;

public:

    Person(int value):value(value){

    }

    void operator()(int element) const{
        std::cout<<(element+value)<<std::endl;
    }
};

int main(){
    int a=5;

    Person person(a);

    std::vector<int> tsm={1,2,3,4,5,6,7,8,9};

    std::for_each(tsm.cbegin(),tsm.cend(),person);
  return 0;
}

在C++ 标准库中介绍是这么描述函数对象的

image.png

在C++ 中定义了非常多的已定义函数在 < functional > 这个包下面,就像在上一篇中我们所写到的函数对象一样,他给我们带来了非常多的便利,我们都知道在 set 中所有的数据的排列都是有序的,那么我们如何更改这个排序呢,看下面例子

#include <iostream>
#include <set>
#include "print.cpp"
#include <functional>


int main(){

    std::set<int,std::greater<int>>  tsm={1,3,5,7,9};

    PRENT_ELEMENT(tsm.cbegin(),tsm.cend());


    return 0;
}

结果:

D:\CWorkSpace\tsmTest\cmake-build-debug\tsmTest.exe
9
7
5
3
1

Process finished with exit code 0

如果将 greater 变成less 又会发生什么呢,

没错,就是按照从小到大的顺序排列

D:\CWorkSpace\tsmTest\cmake-build-debug\tsmTest.exe
1
3
5
7
9

Process finished with exit code 0

现在看到的仅仅只是针对int排序,如果针对一个class 来排序呢,看下面例子

#include <iostream>
#include <set>
#include "print.cpp"
#include <functional>


class Tsm{

private:
public:
    int index;
    Tsm(int index):index(index){

    }
};

typedef  struct  TsmCompare{
    bool operator() (const Tsm& tsm1,const Tsm& tsm2){
        return tsm1.index < tsm2.index;
    }
};

int main(){
    std::set<Tsm,TsmCompare>  tsm;
    for (int i = 0; i < 10; ++i) {
        tsm.emplace(i);
    }
    std::for_each(tsm.cbegin(),tsm.cend(),[](const Tsm t){
       std::cout<<t.index<<std::endl;
    });
    return 0;
}

现在这个结构体 TsmCompare 他使用的是 < ,对应的是降序,如果使用 > 对应的就是升序,

我们现在使用的是一个普通的函数,如何将这个函数升级成为更高等级的函数呢, Binder 可以帮助我们来完成这个操作, Binder 可以将预定义函数对象其他数值结合到一起来使用,看下面这个例子

#include <iostream>
#include <set>
#include "print.cpp"
#include <functional>
#include <deque>



int main(){

    std::set<int,std::greater<int>> tsm={5,1,3,4,2,6,9,7,8};

    std::deque<int> tsm2;

    PRENT_ELEMENT(tsm.cbegin(),tsm.cend());

    std::transform(tsm.begin(),tsm.end(),std::inserter(tsm2,tsm2.begin()),std::bind(std::multiplies<int>(),std::placeholders::_1,10));

    std::cout<<"------------------------"<<std::endl;

    PRENT_ELEMENT(tsm2.cbegin(),tsm2.cend());


    return 0;
}

我们先使用降序排列创建了一个set ,又创建了一个deque , 使用 transform 转换,遍历tsm ,在遍历的过程中使用 insertor 将数据插入到tsm2中,最后一个参数

std::bind(std::multiplies<int(),std::placeholders::_1,10) 

他的写法就比较有意思了, 首先 bind 是一个函数,这个函数的作用是包裹其他函数, std::placeholders::_1 是一个占位符,在遍历的过程中拿到的 element 就会赋值给他,再使用 multiplies 做乘法,的结果就是遍历 start -> end ,每个位置都乘以 10

来看一下结果:

D:\CWorkSpace\tsmTest\cmake-build-debug\tsmTest.exe
9
8
7
6
5
4
3
2
1
------------------------
90
80
70
60
50
40
30
20
10

Process finished with exit code 0


标签:函数对象标准


程序开发学习排行
最近发表
网站分类
标签列表