C++ std function和bind的用法

C++ std function和bind的用法

c++内部存在2种函数指针,普通函数指针(和c语言一样)与成员函数指针. 两者的区别在于成员函数指针多了一个对象指针. 普通函数调用本质是通过普通函数指针实现的,成员函数本质也是通过成员函数指针实现的. 成员函数和普通函的区别在于成员函数隐藏了this指针,且该隐藏位置为成员函数参数的起始位置.

如:

class TAg
{
public:
    void set(int a, unsigned int b) //注意:函数的空间和变量的空间分开,函数空间所有类共用,变量空间每个对象都有一份.
    {
        m_a = a;
        m_b = b;
        WDBG("TAg a[%],b[%]", a, b);
    }

        //写各种函数不影响对象空间的大小!!(兼容c语言)
    //set成员函数相当于 void set(TAg *pthis, int a, unsigned int b);  //第一个参数隐藏了

    int m_a; //sizeof(TAg) = 8
    unsigned int m_b;
};

TAg  ag;
ag.set(3, 4);  //实际为  TAg::set(&ag, 3, 4); //第一个隐藏为自己

结论:只要将成员函数第一个参数位置补上this,就和普通函数一致.


std::function作用: 对普通函数指针进行封装的容器,能运行函数.一般和std::bind以前匹配使用. std::bind(绑定)作用: 将其它类型函数输入转为需要的输出函数类型.可以按值或者按引用绑定数据(与仿函数类似),它包含2个方向(调用者和提供者).

调用者:为实际调用接口的语句,只有一种特定的接口方式.为提供者准备当前的参数位置别名,供提供者任意组合使用

如:typedef void (*pd)(int a, in b, in c); //最多29个参数

  • 第一个参数为a 位置缩写:std::placeholders::_1
  • 第二个参数为b 位置缩写:std::placeholders::_2
  • 第三个参数为c 位置缩写:std::placeholders::_3

提供者:实际函数的接口拥有方,可以通过std::bind(绑定)将该接口转换为调用者的方式.

以下为实例说明用法:

class TAg
{
    public:
    void set(int a, unsigned int b) //类成员函数
    {
        m_a = a;
        m_b = b;
        WDBG("TAg a[%],b[%]", a, b);
    }

    int m_a; //sizeof(TAg) = 8
    unsigned int m_b;
};

void seta(int a, unsigned int b) //普通函数
{
   WDBG("set, a[%],b[%]", a, b);
}

typedef std::function<void(int,unsigned int)> TsCb; //void(int,unsigned int) 为模板参数指针的类型, void为返回参数类型,()内部为每个参数的类型.必须要匹配才能编译
//void(int,unsigned int) 为调用者希望的格式
//TAg::set 与 seta 为提供者的格式

void func()
{
    TsCb cb1(&seta); //普通函数调用(一致使用,不需要更改)

    cb1(2, 3);
    //输出为 set, a[2],b[3]
}

void func2()
{
    TAg ab;
    TAg *pab = &ab; //bind的对象必须为指针类型,不要用&ab
    TsCb cb2(std::bind(&TAg::set, pab, std::placeholders::_1, std::placeholders::_2)); //将成员函数变为普通函数
    //注意std::bind可以绑定任意多个参数,也可以按引用或指针捕获(注意生命周期).
    //如果按值时仅为当时值的副本
    //std::placeholders::_1 为:set函数的参数a位置对应 函数调用的第一个位置
    //std::placeholders::_2 为:set函数的参数b位置对应 函数调用的第二个位置  
    //也可以自己反过来安排..

    cb2(2, 3); //调用函数
    //输出为 TAg a[2],b[3]

    if(cb2)  //如果需要判断是否为空  通过 重载 explicit operator bool()
    {
        cb2(2, 3);
    }

    { //反过顺序例子
        TsCb cb3(std::bind(&TAg::set, pab, std::placeholders::_2, std::placeholders::_1)); //将成员函数变为普通函数
        cb3(2, 3); //调用函数 (输入方式没变,但输出变反)
        //输出为 TAg a[3],b[2]
    }

    { //或者都一样, 可以自由安排
        TsCb cb4(std::bind(&TAg::set, pab, std::placeholders::_1, std::placeholders::_1)); //将成员函数变为普通函数
        cb4(2, 3); //调用函数 (输入方式没变,但输出变反)
        //输出为 TAg a[2],b[2]
    }

    { //超过提供范围会报错
        TsCb cb5(std::bind(&TAg::set, pab, std::placeholders::_1, std::placeholders::_3)); //编译不过!!!
    }
}