function可作为方法指针承接方法,这在设计函数回调的时候非常有用,与之配合的好兄弟bind可以绑定方法到function类型
#include <iostream>
#include <functional>
namespace Test {
// 全局方法区
#pragma region functions
int add(int a, int b) { // 一个超简单的方法
return a + b;
}
void TestFunc1(int a, int b) { // 注意这里是复制传入
std::cout << a << "," << b << std::endl;
}
void TestFunc2(int& a) { // 这里是引用传入
std::cout << a << std::endl;
}
struct divide
{
int operator()(int a, int b) { // 重载运算符
return a / b;
}
};
#pragma endregion
// 定义区
#pragma region define
class MyType {
public:
MyType() {};
};
class A {
public:
using CallbackFn = std::function<void(MyType&)>;
static A* Create();
virtual void SetCallback(const CallbackFn& callback) = 0;
};
class B : public A
{
public:
CallbackFn callBack;
inline void SetCallback(const CallbackFn& callback) override { callBack = callback; }
};
A* A::Create() {
return new B();
}
class TestClass
{
public:
A* m_A;
void CallBack(MyType& e);
TestClass();
};
#pragma endregion
// 实现区
#pragma region realize
void TestClass::CallBack(MyType& e) {
std::cout << "called" << std::endl;
}
TestClass::TestClass() {
m_A = A::Create(); // 这里在bind时就必须要传入this参数,具体是为什么不清除,不然编译不过
const Test::B::CallbackFn& callbackFun = std::bind(&TestClass::CallBack, this, std::placeholders::_1);
m_A->SetCallback(callbackFun);
// 用了一种编程手法,new子父接,父中写好纯虚方法,看似操作父实际操作子
B* ww = dynamic_cast<B*>(m_A); // 在调用子类中存储的方法是要先转型,推荐使用dynamic_cast,主要用于类层次间的上下行转换,自动类型检查对齐
MyType* mt = new MyType();
ww->callBack(*mt); // 调用方法要满足方法参数 输出called
}
#pragma endregion
}
int main() {
std::function<int(int, int)> f1 = Test::add;//函数指针
std::function<int(int, int)> f2 = Test::divide();//函数对象类的对象
std::function<int(int, int)> f3 = [](int a, int b) {return a * b; };//lambda表达式
std::cout << f1(1, 2) << std::endl;; // 可以这样直接调用函数指针,和调用函数是一样的 输出3
// bind绑定函数,基础结构是这样的,第一个参数写要绑定的函数名,加不加&好像没区别,注意要标明函数的作用区间(Test::)这个很重要
// 后面的参数是占位符,因为我这个方法要传入两个int类型参数所以用两个占位符
std::function<void(int, int)> bindFunc1 = bind(Test::TestFunc1, std::placeholders::_1, std::placeholders::_2);
bindFunc1(5, 8); // 输出5,8
std::function<void(int&)> bindFunc2 = std::bind(&Test::TestFunc2, std::placeholders::_1);
int temp = 6; // 因为这里是引用传值所以必须传入左值,不能像上面那样了
bindFunc2(temp); // 右值转左值的办法很简单,定义个变量就好了
// 在类的内部使用bind时在参数区不太一样,这里调用了TestClass的构造方法,请移步到方法实现
Test::TestClass* tc = new Test::TestClass();
}
看了或跑了上面的代码后,是不是会出现一个问题,function已经是函数指针了,我为什么需要bind再重新绑定一个函数出来呢?比如这些地方完全可以这么写
std::function<void(int, int)> bindFunc1 = bind(Test::TestFunc1, std::placeholders::_1, std::placeholders::_2);
直接这样替换
std::function<void(int, int)> bindFunc1 = Test::TestFunc1;
确实上面这里使用bind看起来可能是自找麻烦,如果你这么认为那还是对function不够了解,在类中是不能这么赋值的,没错,就是因为那个this参数,如果这里这么替换就会编译报错
const Test::B::CallbackFn& callbackFun = std::bind(&TestClass::CallBack, this, std::placeholders::_1);
替换为下面这行编译不过
const Test::B::CallbackFn& callbackFun = TestClass::CallBack;
所以需要bind的地方就来了,在类中只能采用bind写法,而且,我认为bind真正的便捷体现在宏定义中,比如我定义这样一个宏来处理类中的bind,类外用不用bind无所谓所以不用了,还简洁
#define BIND_EVENT_FN(x) std::bind(&Test::x, this, std::placeholders::_1)
x是直接替换过去的字符串,这样就可以用宏在类中处理大量的bind,在定义上面的宏之后下面这两行是等效的
const Test::B::CallbackFn& callbackFun = std::bind(&TestClass::CallBack, this, std::placeholders::_1);
等效
const Test::B::CallbackFn& callbackFun = BIND_EVENT_FN(TestClass::CallBack);