C++11回调函数与注意事项

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数可以是静态类函数也可以是全局函数

回调函数机制:

  • 定义一个函数(普通函数即可);
  • 将此函数的地址注册给调用者;
  • 特定的事件或条件发生时,调用者使用函数指针调用回调函数

注意事项

  • 全局函数可以直接作为回调函数
  • C++将类函数直接作为回调函数,必须是静态函数

原因:大概原因是普通的C++成员函数都隐含了一个传递函数作为参数,即this指针,C++通过传递this指针给成员函数从而实现函数可以访问C++的数据成员。由于this指针的原因,使得一个普通成员函数作为回调函数时就会因为隐含的this指针问题使得函数参数个数不匹配,从而导致回调函数编译失败。
原文链接

注:C++静态成员函数

  • 与静态数据成员不同,静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员。

静态成员函数没有this指针。既然它没有指向某一对象,也就无法对一个对象中的非静态成员进行默认访问(即在引用数据时不指定对象名)。
静态成员函数和非静态成员函数的根本区别:
非静态成员函数有this指针,而非静态成员函数没有this指针。由此决定了静态成员函数不能访问本类中的非静态成员。
静态成员函数可以直接引用本类中的静态数据成员,静态成员函数主要用来访问静态数据成员,而不访问非静态成员。
原文链接

使用方法

全局函数-C写法

#include <stdio.h>
typedef int(*callback)(int, int);

int add(int a, int b, callback p){
    return (*p)(a, b);
}

int add(int a, int b){
    return a + b;
}
int main(int argc, char *args[]){
    int res = add(4, 2, add);
    printf("%d\n", res);
    return 0;
}

全局函数-C++写法1-(推荐写法)

#include <iostream>
typedef int(*callback)(int, int);
int add(int a, int b)
{
    return a + b;
}

int Print(callback p, int a, int b)
{
    std::cout << p(a, b) << std::endl;
    return 0;
}

int main()
{
    Print(add, 1, 2);
    system("pause");
    return 0;
}

全局函数-C++写法2

#include <iostream>
int add(int a, int b)
{
    return a + b;
}

int Print(int(*callback)(int, int), int a, int b)
{
    std::cout << callback(a, b) << std::endl;
    return 0;
}

int main()
{
    Print(add, 1, 2);
    system("pause");
    return 0;
}

静态类函数

  • 无需指明类对象
  • 只能访问到类的静态变量,(除非将调用对象作为参数传入静态函数,不推荐链接举例
#include <iostream>
typedef int(*callback)(int);
class classA
{
public:
    static int classA::m_i;
    static int classA_cb(int i)//静态函数没有this指针,只能访问到静态成员
    {
        m_i = 0;
        std::cout << i << std::endl;
        return m_i;
    };
};
int classA::m_i = 0; //静态成员是该类所有对象唯一的,静态成员使用之前必须初始化
int test(callback p, int i)
{
    return p(i);
}
int main()
{
    std::cout << test(classA::classA_cb, 1) << std::endl;//因为函数是静态的,所以,使用方式类似全局函数
    system("pause");
    return 0;
}

非静态类函数-指定类并提供调用的对象

非静态函数,必须指定函数所在类,然后传入类的对象。如果是类调用自身的非静态成员函数,则不需要传入对象。

#include <iostream>

class MyClass;
typedef void (MyClass::*CallbackFunc)(int); // 声明回调成员函数指针

class MyClass
{
public:
    void Func(int n)// 回调成员函数
    {
        std::cout << __FUNCTION__ << " n = " << n << std::endl;
    }
    
    void test(CallbackFunc func, int n)// 在类的内部调用
    {
        (this->*func)(n);
    }
};

void test(CallbackFunc func, MyClass *obj, int n)// 在类的外部调用
{
    (obj->*func)(n);
}

int main()
{
    MyClass obj;
    obj.test(&MyClass::Func, 10);//使用类调用类内的函数的方式
    test(&MyClass::Func, &obj, 20);//使用类外调用类内函数的方式
    system("pause");
    return 0;
}

非静态成员函数-模板方式实现

#include <iostream>
class MyClass
{
public:
    void Func(int n)// 回调成员函数
    {
        std::cout << __FUNCTION__ << " n = " << n << std::endl;
    }
};

class OtherClass
{
public:
    
    void Func(int n)// 回调成员函数
    {
        std::cout << __FUNCTION__ << " n = " << n << std::endl;
    }
};

template<typename T, typename A>
void test(void (T::*method)(A), T *obj, const A& a)
{
    (obj->*method)(a);
}

int main()
{
    MyClass obj;
    test(&MyClass::Func, &obj, 10);

    OtherClass other;
    test(&OtherClass::Func, &other, 20);
    system("pause");
    return 0;
}

声明函数指针数组

template <typename PanelClass>
typedef void(PanelClass::*FlashAllAreaCB[10])();

声明函数指针容器-存疑

//会报错,不知为啥
template <typename PanelClass>
typedef void(PanelClass::*FlashAllAreaCB[10])();
std::list<FlashAllAreaCB> mylist;//提示FlashAllAreaCB未定义

示例1-类模板+回调函数

#include <iostream>
#include <list>
class panelClass
{
public:
    void Func1()// 回调成员函数
    {
        std::cout << __FUNCTION__ << " n = " << std::endl;
    }
};

template<typename T>
class classA
{
    typedef void(T::*callback)();
    T * mobj;
    callback mcb;
public:
    classA(callback cb, T * obj);
    void flushs();
};


template<typename T>
classA<T>::classA(callback cb, T * obj)
{
    mcb = cb;
    mobj = obj;
}

template<typename T>
void classA<T>::flushs()
{
    (mobj->*mcb)();
}

int main()
{
    panelClass pc;
    classA<panelClass> ca(&panelClass::Func1, &pc);
    ca.flushs();
    system("pause");
    return 0;
}

示例2-类模板+回调函数

  • 一个类classA中声明另一个类panelClass
  • panelClass中填入需要由其调用的回函数Func1
  • 在需要的时候,panelClass会代替classA调用Func1函数(例如多线程定时调用)
#include <iostream>
#include <list>
template<typename T>
class classA
{
    typedef void(T::*callback)();
    T * mobj;
    callback mcb;
public:
    classA(callback cb, T * obj) :mcb(cb), mobj(obj){};
    void flushs(){ (mobj->*mcb)(); };
};

class panelClass
{
public:
    panelClass(){ ca = new classA<panelClass>(&panelClass::Func1, this); };
    ~panelClass(){ delete ca; ca = NULL; };
    classA<panelClass> * ca;
    void Func1()// 回调成员函数
    {
        std::cout << __FUNCTION__ << " n = " << std::endl;
    }
};

int main()
{
    panelClass pc;
    pc.ca->flushs();
    system("pause");
    return 0;
}