【c++】【线程池】可调用对象包装器
1. 可调用对象的定义
C++ 中的可调用对象是可以被调用的对象,通常有以下几种形式:
1. 函数指针
函数指针是指向函数的指针,可以用于调用函数。它是一种原始的可调用对象类型。
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
int main() {
int (*func_ptr)(int, int) = &add;
cout << func_ptr(3, 4) << endl; // 输出 7
return 0;
}
2. 函数对象(仿函数)
函数对象是实现了 operator()
的对象,它可以像普通函数一样被调用。
#include <iostream>
using namespace std;
struct Adder {
int operator()(int a, int b) {
return a + b;
}
};
int main() {
Adder add;
cout << add(3, 4) << endl; // 输出 7
return 0;
}
3. Lambda 表达式
Lambda 表达式是一种匿名函数,可以快速定义在代码中。它们本质上也是函数对象。
#include <iostream>
using namespace std;
int main() {
auto add = [](int a, int b) { return a + b; };
cout << add(3, 4) << endl; // 输出 7
return 0;
}
4. 成员函数指针
成员函数指针是指向类成员函数的指针。它需要与一个对象实例结合才能调用。
#include <iostream>
using namespace std;
class MyClass {
public:
void print(int a, int b) {
cout << a << " + " << b << " = " << a + b << endl;
}
};
int main() {
MyClass obj;
void (MyClass::*func_ptr)(int, int) = &MyClass::print;
(obj.*func_ptr)(3, 4); // 输出 3 + 4 = 7
return 0;
}
5.可被转换为函数指针的类对象
using func_ptr = void(*)(int, string);
struct Test {
static void print(int a, string b) {
cout << "name: " << b << ", age: " << a << endl;
}
operator func_ptr() { return print; }
};
Test t;
t(19, "Monkey D. Luffy");
6.类成员指针
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Test
{
void print(int a, string b)
{
cout << "name: " << b << ", age: " << a << endl;
}
int m_num;
};
int main(void)
{
// 定义类成员函数指针指向类成员函数
void (Test::*func_ptr)(int, string) = &Test::print;
// 类成员指针指向类成员变量
int Test::*obj_ptr = &Test::m_num;
Test t;
// 通过类成员函数指针调用类成员函数
(t.*func_ptr)(19, "Monkey D. Luffy");
// 通过类成员指针初始化类成员变量
t.*obj_ptr = 1;
cout << "number is: " << t.m_num << endl;
return 0;
}
2. 可调用对象包装器(std::function
)
std::function
是一个模板类,允许我们以统一的方式封装各种类型的可调用对象,如普通函数、成员函数、仿函数等,提供一种更为灵活的调用方式。
std::function必须要包含一个叫做functional的头文件,可调用对象包装器使用语法如下:
2.1 定义
#include <functional>
std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
2.2 基本用法
#include <functional>
int add(int a, int b) {
cout << a << " + " << b << " = " << a + b << endl;
return a + b;
}
class T1 {
public:
static int sub(int a, int b) {
cout << a << " - " << b << " = " << a - b << endl;
return a - b;
}
};
class T2 {
public:
int operator()(int a, int b) {
cout << a << " * " << b << " = " << a * b << endl;
return a * b;
}
};
int main() {
std::function<int(int, int)> f1 = add;
std::function<int(int, int)> f2 = T1::sub;
T2 t;
std::function<int(int, int)> f3 = t;
f1(9, 3);
f2(9, 3);
f3(9, 3);
return 0;
}
通过 std::function
,我们将不同类型的可调用对象封装成统一的接口,使用方式与函数指针类似。
2.3 回调函数使用
std::function
还可以作为回调函数使用,它替代了传统的函数指针,使代码更加灵活和易于管理。
class A {
public:
A(const std::function<void()>& f) : callback(f) {}
void notify() { callback(); }
private:
std::function<void()> callback;
};
class B {
public:
void operator()() { cout << "我是要成为海贼王的男人!!!" << endl; }
};
int main() {
B b;
A a(b);
a.notify();
return 0;
}
通过上面的例子可以看出,使用对象包装器std::function可以非常方便的将仿函数转换为一个函数指针,通过进行函数指针的传递,在其他函数的合适的位置就可以调用这个包装好的仿函数了。
另外,使用std::function作为函数的传入参数,可以将定义方式不相同的可调用对象进行统一的传递,这样大大增加了程序的灵活性。
3. 绑定器(std::bind
)
std::bind
用于将可调用对象和其参数绑定,生成一个新的可调用对象(仿函数)。
绑定器可以将多参数的可调用对象转化为少参数的可调用对象。
3.1定义
// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
3.2 基本用法
#include <iostream>
#include <functional>
using namespace std;
void callFunc(int x, const function<void(int)>& f)
{
if (x % 2 == 0)
{
f(x);
}
}
void output(int x)
{
cout << x << " ";
}
void output_add(int x)
{
cout << x + 10 << " ";
}
int main(void)
{
// 使用绑定器绑定可调用对象和参数
auto f1 = bind(output, placeholders::_1);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f1);
}
cout << endl;
auto f2 = bind(output_add, placeholders::_1);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f2);
}
cout << endl;
return 0;
}
在上面的程序中,使用了std::bind绑定器,在函数外部通过绑定不同的函数,控制了最后执行的结果。std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,在使用的时候我们并不需要关心绑定器的返回值类型,使用auto进行自动类型推导就可以了。
placeholders::_1是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5等……
有了占位符的概念之后,使得std::bind的使用变得非常灵活:
3.3 绑定多个参数
void output(int x, int y) {
cout << x << " " << y << endl;
}
int main() {
bind(output, 1, 2)();
bind(output, std::placeholders::_1, 2)(10); // 输出: 10 2
bind(output, 2, std::placeholders::_1)(10); // 输出: 2 10
return 0;
}
3.4 绑定类成员函数
可调用对象包装器std::function是不能实现对类成员函数指针或者类成员指针的包装的,但是通过绑定器std::bind的配合之后,就可以完美的解决这个问题了
std::bind
还可以用来绑定类成员函数,并与对象一起传递。
class Test {
public:
void output(int x, int y) { cout << "x: " << x << ", y: " << y << endl; }
};
int main() {
Test t;
auto f = std::bind(&Test::output, &t, std::placeholders::_1, std::placeholders::_2);
f(520, 1314); // 输出: x: 520, y: 1314
return 0;
}
总结
std::function
用来包装各种类型的可调用对象,使其具有统一的接口。std::bind
用来将可调用对象和其参数绑定,生成一个新的可调用对象(仿函数),使得函数调用更加灵活。- 占位符(
std::placeholders
) 是std::bind
中的占位符,用来指定绑定函数参数的位置。
参考: https://subingwen.cn/cpp/bind/#3-%E7%BB%91%E5%AE%9A%E5%99%A8