C++ 仿函数

作者: zcwfeng | 来源:发表于2021-04-02 22:48 被阅读0次

    仿函数

    定义:仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator这个类就有了类似函数的行为,就是一个仿函数类了。

    有时会发现有些功能实现的代码,会不断的在不同的成员函数中用到,但是又不好将这些代码独立出来成为一个类的一个成员函数。但是又很想复用这些代码。

    写一个公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就要单立出一个函数,也不是很好维护。

    这时就可以用仿函数了,写一个简单类,除了那些维护一个[类的成员函数]外,就只是实现一个operator(),在类实例化时,就将要用的,非参数的元素传入类中。这样就免去了对一些公共变量的全局化的维护了。又可以使那些代码独立出来,以便下次复用。

    而且这些仿函数,还可以用关联,聚合,依赖的类之间的关系,与用到他们的类组合在一起,这样有利于资源的管理(这点可能是它相对于函数最显著的优点了)。如果在配合上模板技术和policy编程思想,那就更是威力无穷了

    #include <iostream>
    #include <set>
    #include <algorithm>
    using namespace std;
    
    class CompareObject{
    public:
        void operator()(){
            cout << "仿函数" << endl;
        }
    
        void operator()(int number,int number2){
            cout << "仿函数" << endl;
        }
    };
    
    // 查看c++ for_each源码自定义
    class ShowActionObj{
    public:
        void operator()(int content){
            cout << "custom 仿函数" << content << endl;
        }
    };
    // 回调方式
    void showAction(int content){
        cout << "custom 一元谓词" << content << endl;
    
    }
    
    int main(){
    
        // 谓词 == 仿函数
    
        CompareObject fun1;
    
        fun1();
    
    
        set<int> setVar;
        setVar.insert(10);
        setVar.insert(20);
        setVar.insert(30);
        setVar.insert(40);
        setVar.insert(50);
        setVar.insert(60);
    
        for_each(setVar.begin(),setVar.end(),ShowActionObj());
        cout << "---" << endl;
        for_each(setVar.begin(),setVar.end(),showAction);
    
        return 0;
    }
    

    再写C++ STL 中总结了谓词,相当于仿函数
    谓词 <-> 仿函数(空谓词 一元谓词 二元谓词 三元谓词)

    C#是通过委托delegate来实现仿函数的。
    Java中的仿函数是通过实现包含单个函数的接口实现的
    C语言使用[函数指针]和[回调函数]来实现仿函数,例如一个用来排序的函数可以这样使用仿函数

    List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12");
    Comparator<String> numStringComparator =new Comparator<String>(){
        publicint compare(String o1, String o2){
            returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2));
        }
    };
    Collections.sort(list, numStringComparator);
    
    #include <stdlib.h>
    /* Callback function */
    int compare_ints_function(void*A,void*B)
    {
        return*((int*)(A))<*((int*)(B));
    }
    /* Declaration of C sorting function */
    void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*));
    int main(void)
    {
        int items[]={4,3,1,2};
        sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function);
        return 0;
    }
    

    回调函数,谓词,仿函数 分析

    #include <iostream>
    #include <set> // STL包
    #include <algorithm> // 算法包
    
    using namespace std;
    
    // 我如何阅读C++源码,来写我们的仿函数
    // 明明白白的仿函数(一元谓词==一元函数对象)
    class showActionObj {
    public:
        void operator()(int content) {
            cout << "自定义仿函数" << content << endl;
        }
    };
    
    // 回调函数 如果叫 仿函数 有点合理
    // 简洁方式(回调函数、一元谓词      但是不能称为 仿函数)
    void showAction(int content) {
        cout << "自定义 一元谓词" << content << endl;
    }
    
    using namespace std;
    
    int main() {
        set<int> setVar;
    
        setVar.insert(10);
        setVar.insert(20);
        setVar.insert(30);
        setVar.insert(40);
        setVar.insert(50);
        setVar.insert(60);
    
        // for_each(setVar.begin(), setVar.end(), showActionObj());
    
        for_each(setVar.begin(), setVar.end(), showAction);
    
        return 0;
    }
    

    C++ 中,STL + 算法包 + 迭代器 是分开的。所以我们需要手动组合。

    image.png

    for_each: 源码


    for_each.png
    • 回调函数 (功能够简单)
    • 仿函数(扩展性强) C++内置源码使用仿函数频率高,扩展性强
    #include <iostream>
    #include <set> // STL包
    #include <algorithm> // 算法包
    
    using namespace std;
    
    // 回调函数 (功能够简单)
    void showAction(int __first) {
        cout << "一元谓词" << __first << endl;
    }
    
    // 仿函数(扩展性强) C++内置源码使用仿函数频率高,扩展性强
    class showActionObj {
    public:
        int count = 0;
        void _count() { cout << "本次输出次数是:" << this->count << endl; }
    
        void operator() (int __first) {
            cout << "仿函数" << __first << endl;
            count++;
        }
    };
    
    int main() {
        // 理解:类型传递
        // set<int, showActionObj> setVar; 这样写的语法是OK的,不能加括号
        set<int> setVar;
    
        setVar.insert(10);
        setVar.insert(20);
        setVar.insert(30);
        setVar.insert(40);
        setVar.insert(50);
        setVar.insert(60);
    
        // TODO 第一种方式
        for_each(setVar.begin(), setVar.end(), showAction);
        // 请你统计打印次数? 答:做不到
    
        // TODO 第二种方式
        showActionObj s; // 理解:值传递
        s = for_each(setVar.begin(), setVar.end(), s); // 传入进去的s是新的副本,我们外面的s是旧地址
        // 请你统计打印次数? 答:OK
        s._count();
    
        return 0;
    }
    

    看下_Function


    image.png

    可以解释这句话

    s = for_each(setVar.begin(), setVar.end(), s); // 传入进去的s是新的副本,我们外面的s是旧地址

    仿函数 能做到的封装,回调函数是做不到的。源码用到了大量仿函数,证明它的扩展性好。

    解决 赋值问题说明

    类型传递仿函数 怎么看源码得知写法

    set<string> setVar; 
    

    点击进入源码查看

    看到这部分模板定义

    template <class _Key, class _Compare = less<_Key>,
              class _Allocator = allocator<_Key> >
    

    第一个参数就是我们<> 里面的类型,第二个参数,可以不写,默认参数less
    less ,public继承了binary_function

    struct _LIBCPP_TEMPLATE_VIS less : binary_function<_Tp, _Tp, bool>
    {
        _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
        bool operator()(const _Tp& __x, const _Tp& __y) const
            {return __x < __y;}
    };
    

    这样做,思想和RxJava一致,做到统一过滤用,有成百上千的操作符。
    父类就可以得到我的子类仿函数,根据这个,我们就可以自定义仿函数。仿造源码,写一个二元谓词

    #include <iostream>
    #include <set>
    using namespace std;
    
    // C++源码:typename _Compare = std::less   less内置的仿函数,根据内置仿函数去写 自定义
    //  bool operator()(const _Tp& __x, const _Tp& __y) const 二元谓词
    class CompareObjectClass {
    public:
        bool operator() (const string & __x, const string & __y) const { // const 指针 const  常量指针常量 = 只读
            return __x > __y;
        }
    };
    
    int main() {
        set<string, CompareObjectClass> setVar; // 第一版
        setVar.insert(setVar.begin(), "AAAAAAA");
        setVar.insert(setVar.begin(), "BBBBBBB");
        setVar.insert(setVar.begin(), "CCCCCCC");
        setVar.insert(setVar.begin(), "DDDDDDD");
        setVar.insert(setVar.begin(), "EEEEEEE");
        setVar.insert(setVar.begin(), "FFFFFFF");
        // 迭代器 循环
        for (set<string>::iterator iteratorVar = setVar.begin(); iteratorVar != setVar.end(); iteratorVar++) {
            cout << "循环item:" << *iteratorVar  << "\t";
            // 循环item:AAAAAAA   循环item:BBBBBBB  循环item:CCCCCCC  循环item:DDDDDDD  循环item:EEEEEEE  循环item:FFFFFFF
    
            // 循环item:FFFFFFF   循环item:EEEEEEE  循环item:DDDDDDD  循环item:CCCCCCC  循环item:BBBBBBB  循环item:AAAAAAA
        }
        return 0;
    }
    

    通过Set 的内置反函数less, 内置反函数,定义我们自己的函数

    容器存储对象,生命周期

    set 存入对象 奔溃(set会自动排序,对象没法排序,所以奔溃) 解决方案:自定义仿函数解决

    为了方便避免重复,我们用vector存储对象,说明生命周期

    #include <iostream>
    #include <set> // set 存入对象 奔溃(set会自动排序,对象没法排序,所以奔溃)  解决方案:自定义仿函数解决
    #include <vector>  // 存入对象
    
    using namespace std;
    
    class Person {
    private:
        string name;
    public:
        Person(string name) : name(name) {}
    
        void setName(string name) {
            this->name = name;
        }
    
        string getName() {
            return this->name;
        }
    
        Person(const Person &person) {
            this->name = person.name; // 浅拷贝
    
            cout << "Person拷贝构造函数执行了..." << endl;
        }
    
        ~Person() {
            cout << "Person析构函数执行了" << endl;
        }
    };
    
    int main() {
        // Java:把对象存入 添加到 集合
        // C++: 调用拷贝构造函数,存进去的是另一个新的对象
    
        vector<Person> vectorVar;
    
        // person 被main函数弹栈 析构一次
        Person person("David"); // 2  David
    
        // 里面的insert函数弹栈 析构一次
        vectorVar.insert(vectorVar.begin(), person); // 外面的person是旧地址,到insert函数里面的person就是新地址(拷贝构造函数 一次)
    
        person.setName("Kevin"); // 1
    
        // newPerson 被main函数弹栈 析构一次
        Person newPerson =
                vectorVar.front(); // front里面的person是旧地址, 外面的newPerson就是新地址(拷贝构造函数 一次)
    
        cout << "newPerson:" << newPerson.getName().c_str() << endl;
    
        // 3次析构函数   两次拷贝构造
    
        return 0;
    } // main弹栈
    

    代码执行了,3次析构函数 两次拷贝构造

    Person person("David")

    Person newPerson =vectorVar.front()

    执行拷贝构造函数 front里面的是旧的地址,外面的newPerson 就是新的地址

    Person person("David")

    main函数执行完会析构一次

    vectorVar.insert(vectorVar.begin(), person) i

    nsert 内部结束也会析构一次

    Person newPerson =vectorVar.front()

    newPerson 被main函数弹栈 析构一次

    预定义函数

    C++ 内置函数

    #include <iostream>
    #include <set> // STL包
    #include <algorithm> // 算法包
    using namespace std;
    
    int main() {
        // "David" + "AAAA" // 运算符重载
    
        // C++已经提供了 预定义函数  plus,minus,multiplies,divides,modulus ...
        plus<int> add_func;
    
        int r = add_func(1, 1);
        cout << r << endl;
    
        plus<string> add_func2;
        string r2 = add_func2("AAAA", "BBB");
        cout << r2 << endl;
    
        plus<float> add_func3;
        float r3 = add_func3(4354.45f, 34.3f);
        cout << r3 << endl;
    
        return 0;
    }
    

    简单的例子,plus 。 我们拼接字符串可能想到运算符重载,但是c++内置plus解决了这个问题。

    有些比如,对象可能就不适用,我们可以手写预定义函数
    : public binary_function_t<T, T, T>
    可以要可以不要

    #include <iostream>
    #include <set> // STL包
    #include <algorithm> // 算法包
    using namespace std;
    
    template<typename Arg1, typename Arg2, typename Result>
    struct binary_function_t
    {
        /// 第一个参数类型 是底一个参数的类型
        typedef Arg1    first_argument_type;
    
        //econd_argument_type是第二个参数的类型
        typedef Arg2    second_argument_type;
    
        /// @c result_type是返回类型
        typedef Result  result_type;
    };
    
    // TODO 对象 + 对象
    // 1.运算符重载
    // 2.对象+对象 自己去写仿函数
    
    template<typename T>
    struct plus_d : public binary_function_t<T, T, T>
    {
        T operator() (const T & x, const T & y) {
            return x + y;
        }
    };
    
    int main() {
    
        plus_d<int> add_func;
        int r = add_func(1, 1);
        cout << r << endl;
    
        plus_d<string> add_func2;
        string r2 = add_func2("AAAA", "BBB");
        cout << r2 << endl;
    
        plus_d<float> add_func3;
        float r3 = add_func3(4354.45f, 34.3f);
        cout << r3 << endl;
    
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:C++ 仿函数

        本文链接:https://www.haomeiwen.com/subject/fpxnhltx.html