新手学C/C++:一篇文章予你分享现代C++的回调技术--std::bind+std::function

参考自《Linux多线程服务端编程》以及muduo源码,对其中的一些实现细节有着十分深刻的印象,尤其是使用std::bind和std::function的回调技术。可以说,这两个大杀器简直就是现代C++的“任督二脉”,甚至可以解决继承时的虚函数指代不清的问题。在此详细叙述使用std::bind和std::function在C++对象之间的用法,用以配合解决事件驱动的编程模型。

本文组成:

1.std::function

2.std::bind

3.使用std::bind和std::function的回调技术

4.std::bind绑定到虚函数时会表现出多态行为,解决继承时的虚函数指代不清的问题

(文字不多,主要是代码,有7个独立的cpp,路过的朋友可以拷贝到自己环境中观察运行结果。文中代码已实测)

 

function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却却比函数指针更加灵活,特别是函数指向类 的非静态成员函数时。不过网上有文章简单测试:函数指针要比直接调用慢2s左右;std::function 要比函数指针慢2s左右 。该测试结果链接:https://cherishlc.iteye.com/blog/2002095

 

std::function

std::function包含于头文件 #include<functional>中,可将各种可调用实体进行封装统一,包括

1.普通函数

2.lambda表达式

3.函数指针

4.仿函数(functor 重载括号运算符实现)

5.类成员函数

6.静态成员函数

下面source.cpp实例通过上述几种方式实现一个简单的比较两个数的大小的功能(读者可拷贝代码观察结果):

source.cpp:

 

#include <iostream>

#include <functional> 

using namespace std; 

std::function<bool(int, int)> fun;

//1.普通函数

bool compare_com(int a, int b)

{

    return a > b;

}

//2.lambda表达式

auto compare_lambda = [](int a, int b){ return a > b;};

//3.仿函数

class compare_class

{

public:

    bool operator()(int a, int b)

    {

        return a > b;

    }   

};

//4.类成员函数(动态、静态)

class compare

{

public:

    bool compare_member(int a, int b)

    {

        return a > b;

    }

    static bool compare_static_member(int a, int b)

    {

        return a > b;

    }

};

//对应的main函数如下:

int main()

{

    bool result;

    fun = compare_com;

    result = fun(10, 1);

    cout << "普通函数输出, result is " << result << endl;

 

    fun = compare_lambda;

    result = fun(10, 1);

    cout << "lambda表达式输出, result is " << result << endl;

 

    fun = compare_class();

    result = fun(10, 1);

    cout << "仿函数输出, result is " << result << endl;

 

    fun = compare::compare_static_member;

    result = fun(10, 1);

    cout << "类静态成员函数输出, result is " << result << endl;

 

    类普通成员函数比较特殊,需要使用bind函数,并且需要实例化对象,成员函数要加取地址符

    compare temp;

    fun = std::bind(&compare::compare_member, temp, std::placeholders::_1, std::placeholders::_2);

    result = fun(10, 1);

    cout << "类普通成员函数输出, result is " << result << endl;

}

std::bind

std::bind函数将可调用对象(开头所述6类)和可调用对象的参数进行绑定,返回新的可调用对象(std::function类型,参数列表可能改变),返回的新的std::function可调用对象的参数列表根据bind函数实参中std::placeholders::_x从小到大对应的参数确定。

下面source2.cpp实例详细说明返回的新的std::function可调用对象的参数列表如何确定:

source2.cpp:

 

#include <functional>

#include <iostream>

 

using namespace std;

struct Int 

{

    int a;

};

 

bool compare_com(struct Int a, float b)

{

    return a.a > b;

}

 

int main()

{

    Int a = {1};

    //placeholders::_1对应float, placeholders::_2对应struct Int所以返回值fun的类型为function<bool(float, Int)>

    std::function<bool(float, struct Int)> fun = bind(compare_com, placeholders::_2, placeholders::_1);

    bool result = fun(2.0, a);

    cout << "result is " << result << endl;

    return 0;

}

 

使用std::bind和std::function的回调技术

C++的类成员函数不能像普通函数那样用于回调,因为每个成员函数都需要有一个对象实例去调用它。

通常情况下,要实现成员函数作为回调函数:

一种过去常用的方法就是把该成员函数设计为静态成员函数(因为类的成员函数需要隐含的this指针 而回调函数没有办法提供),但这样做有一个缺点,就是会破坏类的结构性,因为静态成员函数只能访问该类的静态成员变量和静态成员函数,不能访问非静态的,要解决这个问题,可以把对象实例的指针或引用做为参数传给它。后面就可以靠这个对象实例的指针或引用访问非静态成员函数。

另一种办法就是使用std::bind和std::function结合实现回调技术。

 

下面的所有讨论基于对象。

 

bind和function结合的基础用法

source3.cpp:

 

#include<iostream>

#include<functional>

typedef std::function<void()> Functor;

class Blas

{

    public:

        void add(int a,int b)

        {

            std::cout << a+b << std::endl;

        }

 

        static void addStatic(int a,int b)

        {

            std::cout << a+b << std::endl;

        }

};

 

int main(int argc,char** argv)

{

    Blas blas;

 

//使用bind绑定类静态成员函数

    Functor functor(std::bind(&Blas::addStatic,1,2));

 

//使用bind绑定类的chengyuan函数

    Functor functor2(std::bind(&Blas::add,blas,1,2));

 

    functor();

    functor2();

    return 0;

}

上述代码中的区别是:如果不是类的静态成员函数,需要在参数绑定时,往绑定的参数列表中加入使用的对象。

 

使用function和bind实现回调功能

source4.cpp:

 

#include<iostream>

#include<functional>

 

typedef std::function<void()> Functor;

 

class Blas

{

    public:

        void setCallBack(const Functor& cb)

        {functor = cb;};

 

        void printFunctor()

        {functor();};

    private:

        Functor functor;

};

 

class Atlas

{

    public:

        Atlas(int x_) : x(x_)

        {

            //使用当前类的静态成员函数

 

           blas.setCallBack(std::bind(&addStatic,x,2));

        //使用当前类的非静态成员函数 

                  blas.setCallBack(std::bind(&Atlas::add,this,x,2));

        }

 

        void print()

        {

            blas.printFunctor();

        }

    private:

        void add(int a,int b)

        {

            std::cout << a+b << std::endl;

        }

 

        static void addStatic(int a,int b)

        {

            std::cout << a+b << std::endl;

        }

 

        Blas blas;

        int x;

};

 

int main(int argc,char** argv)

{

    Atlas atlas(5);

    atlas.print();

    return 0;

}

在以上代码中的

void add();

void addStatic();

两个函数在Atlas类中,并且可以自由操作Atlas的数据成员。尽管是将add()系列的函数封装成函数对象传入Blas中,并且在Blas类中调用,但是它们仍然具有操作Atlas数据成员的功能,在两个类之间形成了弱的耦合作用。但是如果要在两个类之间形成弱的耦合作用,必须在使用std::bind()封装时,向其中传入this指针:

 

std::bind(&Atlas::add,this,5,2);

也就是说,要在两个类之间形成耦合作用,要使用非静态的成员函数(私有和公有都可以)。代码如下:

source5.cpp:

 

#include<iostream>

#include<functional>

 

typedef std::function<void()> Functor;

 

class Blas

{

    public:

        void setCallBack(const Functor& cb)

        {functor = cb;};

 

        void printFunctor()

        {functor();};

    private:

        Functor functor;

};

 

class Atlas

{

    public:

        Atlas(int x_,int y_) : x(x_),y(y_)

        {

            //使用当前类的非静态成员函数

                        blas.setCallBack(std::bind(&Atlas::add,this,x,2));

        }

        void print()

        {

            blas.printFunctor();

        }

 

    private:

        void add(int a,int b)

        {

            std::cout << y << std::endl;

            std::cout << a+b << std::endl;

        }

        Blas blas;

        int x,y;

};

 

int main(int argc,char** argv)

{

    Atlas atlas(5,10);

    atlas.print();

    return 0;

}

 

这样,便可以Atlas便可以在Blas类中注册一些函数对象,这些函数对象在处理Blas数据的同时(在std::bind中预留位置传入Blas的参数),还可以回带处理Atlas的数据,形成回调作用。代码如下:

source6.cpp:

 

#include<iostream>

#include<functional>

 

typedef std::function<void(int,int)> Functor;

 

class Blas

{

    public:

        void setCallBack(const Functor& cb)

        {functor = cb;};

 

        void printFunctor()

        {functor(x,y);};

 

    private:

        int x = 10;

        int y = 10;

        Functor functor;

};

class Atlas

{

    public:

        Atlas(int x_,int y_) : x(x_),y(y_)

        {

           //使用当前类的非静态成员函数

               blas.setCallBack(std::bind(&Atlas::add,this,std::placeholders::_1,std::placeholders::_2));

        }

        void print()

        {

            blas.printFunctor();

        }

    private:

        void add(int a,int b)

        {

            std::cout << y << std::endl;

            std::cout << a+b << std::endl;

        }

        Blas blas;

        int x,y;

};

int main(int argc,char** argv)

{

    Atlas atlas(5,10);

    atlas.print();

    return 0;

}

 

std::bind绑定到虚函数时会表现出多态行为,解决继承时的虚函数指代不清的问题

source7.cpp:

 

#include <iostream>  

#include <functional>  

using namespace std;  

  

typedef std::function<void ()> fp;  

  

class A  

{  

public:  

    virtual void f()  

    {  

        cout<<"A::f()"<<endl;  

    }  

  

    void init()  

    {  

        //std::bind可以表现出多态行为  

        fp f=std::bind(&A::f,this);  

        f();  

    }  

};  

class B:public A  

{  

public:  

    virtual void f()  

    {  

        cout<<"B::f()"<<endl;  

    }  

};  

int main()  

{  

    A* pa=new B;  

    pa->init();  

  

    return 0;  

}  

文章参考链接:

https://blog.youkuaiyun.com/xiaoyink/article/details/79348806

https://blog.youkuaiyun.com/changyang208/article/details/71653951

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值