作者:平凡小迪 | 来源:互联网 | 2024-12-13 20:50
本文介绍了C++中的可调用对象概念,包括函数指针、仿函数、类成员函数指针等,并详细探讨了`std::function`和`std::bind`在统一处理这些可调用对象时的作用和实现方法。
### 可调用对象
在 C++ 中,可调用对象是指可以像普通函数一样调用的对象。具体来说,可调用对象可以是以下几种形式之一:
1. **函数指针**:指向函数的指针。
2. **仿函数**:具有 `operator()` 成员函数的类对象。
3. **可转换为函数指针的类对象**:可以通过某种方式转换为函数指针的类对象。
4. **类成员函数指针**:指向类成员函数的指针。
下面是一些示例代码:
```cpp
void func() {
cout <<"func" <
}
struct Foo {
void operator()() { cout <<"Foo::operator()" <};
struct Bar {
using fr_t = void(*)();
static void func() { cout <<"Bar::func" < operator fr_t() { return func; }
};
struct A {
int a_;
void mem_func() { cout <<"A::mem_func" <};
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函数地址
A aa;
(aa.*mem_func_ptr)(); // 调用类成员函数
return 0;
}
```
### 可调用对象包装器——std::function
`std::function` 是 C++11 引入的一个模板类,用于包装各种可调用对象。它可以容纳除类成员函数指针以外的所有可调用对象,并允许以统一的方式处理这些对象。
```cpp
void fun() {
cout <<__FUNCTION__ <}
class Foo {
public:
static int foo_func(int a) {
cout <<__FUNCTION__ <<"(" < return 0;
}
};
class Bar {
int val;
public:
Bar(int x = 0) : val(x) {}
int operator()(int a) const {
cout <<__FUNCTION__ <<"(" < return a;
}
};
int main() {
std::function fr1 = fun;
fr1();
std::function fr2 = Foo::foo_func;
fr2(10);
Bar bar(12);
Bar bac(23);
fr2 = bar;
fr2(12);
fr2 = bac;
fr2(34);
return 0;
}
```
### std::bind 绑定器
`std::bind` 用于将可调用对象与其参数一起绑定,形成一个新的可调用对象。绑定后的结果可以使用 `std::function` 进行保存,并延迟调用到任何需要的时候。
`std::bind` 主要有两个作用:
1. 将可调用对象与其参数一起绑定成一个仿函数。
2. 将多元(参数个数为 n,n > 1)可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。
```cpp
void call_when_even(int x, const std::function& f) {
if (!(x & 1)) {
f(x);
}
}
void output(int x) {
cout <}
void output_add_2(int x) {
cout <}
int main() {
{
auto fr = std::bind(output, std::placeholders::_1);
for (int i = 0; i <10; ++i) {
call_when_even(i, fr);
}
}
cout < {
auto fr = std::bind(output_add_2, std::placeholders::_1);
for (int i = 0; i <10; ++i) {
call_when_even(i, fr);
}
}
return 0;
}
```
`std::bind` 可以看作是一个仿函数,如果在 `bind` 中给出了参数,则后面不再需要给出参数;如果给出的是占位符,则需要在调用时给出参数。
```cpp
void output(int x, int y) {
cout <}
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;
}
```
### 实际应用示例
下面是一个实际应用示例,展示了如何使用 `std::bind` 和 `std::function` 来处理任务队列:
```cpp
void output(int x, int y, int& z) {
cout <<"x: " < z = x + y;
}
using Task = std::function;
std::list g_tasklist;
void thread_func() {
cout <<"thread begin" < 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 < return 0;
}
```
通过 `std::bind` 和 `std::function`,我们可以更灵活地处理各种可调用对象,提高代码的复用性和可维护性。