概念
在C++中,存在“可调用对象”这么一个概念,准确来说,可调用对象有如下几种定义:
- 是一个函数指针
- 是一个具有operator()成员函数的类对象(仿函数)
- 是一个可被转换为函数指针的类对象
- 是一个类成员(函数)指针
void func()
{
cout << "func" << endl;
}
struct Foo
{
void operator()()
{
cout << "Foo::operator()" << endl;
}
};
struct Bar
{
using fr_t = void(*)(void); //函数指针类型
static void func()
{
cout << "Bar::func" << endl;
}
operator fr_t() //将对象通过该方法,强转为一个指针
{
return func;
}
};
struct A
{
int a_;
void mem_func()
{
cout << "A::meme_func" << endl;
}
};
int main()
{
void(*func_ptr)() = &func; //1 指向函数指针
func();
Foo foo;
foo(); //2 定义对象 重载()
Bar bar;
bar(); //3 调动重载成为函数指针,返回一个静态函数地址,加()进行调用
void (A:: * mem_func_ptr)() = &A::mem_func; //函数指针指向A作用域下mem_func函数地址
int A::* mem_obj_ptr = &A::a_;//仅仅是一个偏移量
A aa; //普通成员方法需要使用对象 进行激活,静态成员方法不需要
(aa.*mem_func_ptr)(); // .* 调用类成员函数
(aa.*mem_obj_ptr) = 10;
return 0;
}
从上述可以看到,除了类成员指针之外,上面定义涉及的对象均可以像一个函数那样做调用操作
在C++11中,像上面例子中的这些对象都被称作可调用对象,相对应的,这些对象的类型被统称为“可调用类型”
C++中的可调用对象虽然具有比较统一的操作形式(除了类成员指针之外,都是后面加括号进行调用),但定义方法五花八门,这样在我们试图使用统一的方式保存,或传递一个可调用对象时,会十分繁琐
现在,C++11 通过提供std::function 和 std::bind 统一了可调用对象的各种操作
可调用对象包装器——std::function
std::function 是可调用对象的包装器,它是一个模板类,可以容纳除了类的成员(函数)指针外的所有可调用对象,通过指定它的模板参数,它可以用统一的方式处理函数,函数对象,函数指针,并允许保存和延迟执行它们
void fun(void)
{
cout << __FUNCTION__ << endl;
}
class Foo
{
public:
static int foo_func(int a)
{
cout << __FUNCTION__ << "(" << a << ")" << endl;
return 0;
}
};
class Bar
{
int val;
public:
Bar(int x = 0):val(x){}
int operator()(int a) const
{
cout << __FUNCTION__ << "(" << val << ")" << endl;
return a;
}
};
int main()
{
std::function<void(void)> fr1 = fun;
fr1();
std::function<int(int)> fr2 = Foo::foo_func;
fr2(10);
Bar bar(12);
Bar bac(23);
fr2 = bar;
fr2(12);
fr2 = bac;
fr2(34);
}
回调形式
int func(int a = 10)
{
cout << __FUNCTION__ << endl;
a += 10;
cout << a << endl;
return a;
}
int main()
{
std::function<int(int)> fr = func;
int x = fr(); //error!!! 需要参数
return 0;
}
绑定器并不认为可以得到一个默认值
void fun(int a, int b, int (*pfun)(int, int))
{
if (pfun != nullptr)
{
pfun(a, b); //通过函数指针,进行回调方案
}
}
class A
{
std::function<void()> callback_;
public:
A(const std::function<void()>& f) :callback_(f) {}
void notify(void)
{
callback_(); //执行
}
};
class Foo
{
public:
void operator()()
{
cout << __FUNCTION__ << endl;
}
};
int main()
{
Foo foo;
A aa(foo); //传入foo
aa.notify(); //通知
return 0;
}
打印偶数
void call_when_even(int x, const std::function<void(int)>& f)
{
if (!(x & 1))
{
f(x);
}
}
void output(int x)
{
cout << x << " " << endl;
}
int main()
{
for (int i = 0; i < 10; ++i)
{
call_when_even(i, output);
}
return 0;
}
std::bind 绑定器
std::bind 用来将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候
通俗来讲,它主要有两大作用:
- 将可调用对象与其参数一起绑定成一个仿函数
- 将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1) 元可调用对象,即只绑定部分参数
void call_when_even(int x, const std::function<void(int)>& f)
{
if (!(x & 1))
{
f(x);
}
}
void output(int x)
{
cout << x << " " ;
}
void output_add_2(int x)
{
cout << x + 2 << " " ;
}
int main()
{
{
auto fr = std::bind(output, std::placeholders::_1);
// 函数 占位符
for (int i = 0; i < 10; ++i)
{
call_when_even(i, fr);
// 参数
}
}
cout << endl;
{
auto fr = std::bind(output_add_2, std::placeholders::_1);
for (int i = 0; i < 10; ++i)
{
call_when_even(i, fr);
}
}
}
在这里,我们使用了std::bind,在函数外部通过绑定不同的函数,控制了最后的执行结果
我们使用auto fr 保存 std::bind 的返回结果,是因为我们并不关心 std::bind 真正的返回类型(实际上std::bind 的返回类型是一个stl内部定义的仿函数类型),只需要知道它是一个仿函数,可以直接赋值给一个std::function,当然,这里直接使用std::function类型来保存std::bind 的返回值也是可以的
std::placeholders::_1 是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代
void output(int x, int y)
{
cout << x << " " << y << endl;
}
void fun()
{
cout << __FUNCTION__ << endl;
}
void func(int x)
{
cout << __FUNCTION__ << " " << x << endl;
}
int main()
{
int a = 10;
//std::bind(output)(1, 2);
std::bind(func,std::placeholders::_1)(2); //使用占位符,后面给出了参数
std::bind(func, 12)(); //直接给出了参数
std::bind(func, a)();
return 0;
}
可以将bind看作是一个仿函数,并且若在bind中给出了参数,后面则不需要再给定参数,而若给出的是一个占位符,则需要后面给出参数
class Alloc
{
size_t sz;
int*& ptr;
public:
Alloc(size_t s,int*& p):sz(s),ptr(p){}
void operator()()
{
ptr = (int*)malloc(sz);
}
};
int main()
{
int* p = nullptr;
int sz = sizeof(int) * 10;
auto fr = std::bind(alloc, sz, std::ref(p));
fr();
if (p != nullptr)
{
cout << p << endl;
}
}
void output(int x, int y)
{
cout << x << " " << y << endl;
}
int main()
{
std::bind(output, std::placeholders::_1, std::placeholders::_2)(1, 2);
std::bind(output, std::placeholders::_2, std::placeholders::_1)(1, 2);
return 0;
}
void output(int x, int y)
{
cout << x << " " << y << endl;
}
int main()
{
auto fr1 = std::bind(output, std::placeholders::_1, 23);
auto fr2 = std::bind(output, 12, std::placeholders::_2);
fr1(23);
fr2(12); //error!!! 这样是错误的
return 0;
}
bind 函数要么指明参数要么通过占位符进行关联
void output(int x, int y,int& z)
{
cout << "x: " << x << "y: " << y << endl;
z = x + y;
}
using Task = std::function<void(void)>;
std::list<Task> g_tasklist;
void thread_func()
{
cout << "thread begin" << endl;
while (!g_tasklist.empty())
{
Task task = g_tasklist.front();
g_tasklist.pop_front();
task(); //调动事务
//这是一个方法与数据的集合
}
}
int main()
{
int a = 10, b = 20;
int z = 0;
Task t1 = std::bind(output, a, b, std::ref(z));
g_tasklist.push_back(t1);
std::thread tha(thread_func);
tha.join();
cout << z << endl;
}