【c++】【线程池】可调用对象包装器

发布于:2025-02-23 ⋅ 阅读:(19) ⋅ 点赞:(0)

【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::placeholdersstd::bind 中的占位符,用来指定绑定函数参数的位置。

参考: https://subingwen.cn/cpp/bind/#3-%E7%BB%91%E5%AE%9A%E5%99%A8