• 第12课 std::bind和std::function(3)_std::function可调用对象包装器


    1. std::function

    (1)首先是一个类模板用于包装可调用对象。可以容纳除了类成员(函数)指针之外的所有可调用对象。

    (2)可以将普通函数,lambda表达式和函数对象类统一起来。尽管它们并不是相同的类型,但通过function类模板,可以转化为相同类型的对象(function对象),这样就可以用统一的方式来保存或传递可调用对象

    (3)实现了一套类型消除机制,可用统一的方式处理不同类型的可调用对象

    (4)std::function进一步深化以数据为中心(封装)的面向对象思想(连函数都对象化了)

    【编程实验】std::function作为函数的入参“万能类型”

    #include <iostream>
    #include <functional> //for std::bind & std::function
    
    using namespace std;
    using namespace std::placeholders;
    
    //传统C函数
    int func(int a, int b)
    {
        return a + b;
    }
    
    //仿函数
    class Functor
    {
    public:
        int operator()(int a, int b)
        {
            return a - b;
        }
    };
    
    //类的成员函数
    class Foo
    {
    public:
        static int func(int a, int b)
        {
            return a * b;
        }
        
        int func_common(int a, int b)
        {
            return a * b;
        }
    };
    
    //模板函数
    template < typename T>
    auto template_func (T a ,T b)->decltype(a + b)
    {
        return a + b;
    }
    
    //测试函数
    //1. 这是一个“万能”的函数,可接受不同类型的可调用对象,而不必为他们重载多个版本的test函数
    //2. 可以测试的对象包含各类可调用对象,如普通函数、仿函数、lambda表达式等
    int test(int x, int y, const std::function<int(int, int)>& callableObjects)
    {
        return callableObjects(x, y);
    }
    
    int main()
    {
        //传入普通函数
        using Fn = int(*)(int, int);
        Fn  fn = &func;
        //decltype(func)* fn = &func; 
        cout << "func(3, 4): " << test(3, 4, fn) << endl; //普通函数指针可以直接赋值给std::function
        
        //测试函数模板
        auto tfn = template_func<int>;
        cout << "template_func<int>(3, 4): " << test(3, 4, tfn) << endl;
        
        //测试仿函数
        Functor ftor;
        cout << "Functor(3, 4): " << test(3, 4, ftor) << endl; //仿函数可以直接赋值给std::function
        
        //测试lambda表达式
        auto lbd = [](int a, int b){ return a + b; };
        //lambda表达式可以直接赋值给std::function
        cout << "[](int a, int b){ return a + b }: " << test(3, 4, lbd) << endl;
        
        //测试类的成员函数
        auto memfn = Foo::func;  //静态成员函数
        //静态成员函数可以直接赋值给std::function
        cout << "Foo::func(3, 4): " << test(3, 4, memfn) << endl;
        //普通成员函数(不能直接赋值给std::function,需先经bind转成std::function)
        auto commfn = bind(&Foo::func_common, Foo(), _1, _2);//先转换为std::function
        cout << "Foo::func_common(3, 4): " << test(3, 4, commfn) << endl;
        
        return 0;
    }
    /*测试结果
    e:StudyC++1112>g++ -std=c++11 test1.cpp
    e:StudyC++1112>a.exe
    func(3, 4): 7
    template_func<int>(3, 4): 7
    Functor(3, 4): -1
    [](int a, int b){ return a + b }: 7
    Foo::func(3, 4): 12
    Foo::func_common(3, 4): 12
    */

    2. std::function和std::bind的关系

    (1)std::bind是一个函数模板用于将可调用对象及其参数一起,绑定成一个std::function对象。其返回值是个std::function类型。

    (2)std::function是一个类模板,用来包装各类可调用对象为新的callable object。他可以接受全局函数、类的静态成员函数并直接进行封装。但不能直接接受类的非静态成员,需要使用bind绑定才能赋值给std::function。

    【编程实验】利用function+bind实现回调函数类似于函数指针的作用,可保存、延迟处理函数

    #include <iostream>
    #include <functional> //for std::bind & std::function
    
    using namespace std;
    using namespace std::placeholders;
    
    //操作系统
    class OperatingSystem
    {
    private:
        using NotifyFunc = std::function<void(string, string)>;
        NotifyFunc m_callback;
    public:
        string jobname;
        string jobmessage;
    public:
        OperatingSystem() : m_callback(nullptr){};
        
        template <typename T1, typename T2>
        void RegisterNotify(T1 memberFunc, T2* pThis)
        {
            m_callback = std::bind(memberFunc, pThis, _1, _2);
        }
        
        template <typename T>
        void RegisterNotify(T globalFunc)
        {
            m_callback = std::bind(globalFunc, _1, _2);
        }
        
        //当作业加入时,会回调作业本身定义的个性化通知!
        bool Notify()
        {
            if(m_callback != NULL)
                m_callback(jobname, jobmessage);
                
            return false;
        }
    };
    
    //作业类
    class Job
    {
    private:
        
        //每次作业加入进来,会有个性化的通知
        void SendMsg(string name, string msg)
        {
            cout <<"(local)"<< name << ": " << msg << endl;
        }
        
    public:
        string name;
        string msg;
        Job(string name, string msg):name(name),msg(msg)
        {
        }    
        
        void addJob(OperatingSystem& os)
        {
            os.RegisterNotify(&Job::SendMsg, this);
            os.jobname = name;
            os.jobmessage = msg;
            os.Notify();        
        }    
    };
    
    void SendMsg(string name, string msg)
    {
        cout << "(global)" <<name << ": " << msg << endl;
    }
    
    int main()
    {
        OperatingSystem os;
        
        //绑定Job类的成员函数
        Job job1("job1", "hello world!");
        job1.addJob(os);
        
        Job job2("job2", "thank you!");
        job2.addJob(os);
        
        //绑定全局函数
        os.RegisterNotify(&SendMsg);
        os.jobname = "job3";
        os.jobmessage = "nice to meet you!";
        os.Notify();
        
        return 0;
    }
    /*输出结果
    e:StudyC++1112>g++ -std=c++11 test2.cpp
    e:StudyC++1112>a.exe
    (local)job1: hello world!
    (local)job2: thank you!
    (global)job3: nice to meet you!
    */
  • 相关阅读:
    C++之类和对象
    PHP程序设计基础
    PHP函数和MySQL数据库
    HTML语言基础
    文件和目录1(文件属性和权限)
    文件IO
    查找
    使用tcpdump抓包实例
    导入模块的2种方法
    ansible启用sudo执行命令
  • 原文地址:https://www.cnblogs.com/5iedu/p/7635360.html
Copyright © 2020-2023  润新知