在C++工程实践中,bind绑定器和function函数对象非常的重要,依靠他们可以便捷的实现松耦合。
funciotn是从c++11开始支持的特性,使用它需要包含头文件functional
在cppreference中解释为:类模板std::function是一个通用的多态函数包装器。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。
通俗的来说可以把它当做一个函数指针来使用
function的模板是 std::function<返回值类型(传入参数类型)> 方法名
function的作用是将具有相同调用形式的不同类型可调用对象进行类型统一。
相同的调用形式可以简单理解为:参数列表和返回值相同。
C++常见可调用对象有:函数、指针、匿名函数(lambda表达式)、函数对象(重载了函数调用运算符的类)以及使用bind创建的对象。
例如以下几个可调用对象具有相同的调用形式:
// 函数
int add(int a, int b) {
return a + b;
}
// lambda表达式
auto sub = [](int a, int b) -> int {
return a - b;
};
// 函数对象
class prod{
public:
int operator() (int a, int b) {
return a * b;
}
};
这些可调用对象的调用形式相同,甚至拥有相同的功能,但是却由于类型千差万别无法统一处理。
这样说可能有点抽象
function体现在工程实践中的优势可以举个例子来说明
假如我们要设计一个图书管理系统,该系统提供的服务有:借书、查询书、还书。假设这些函数的函数签名都是一样的「即返回值类型和参数类型都是相同的」。我们习惯将服务编号,比如用户输入1表示要获取借书服务,用户输入2表示查询书服务,用户输入3表示还书服务。
此时的开发者有两种设计方案「大体框架,不包括不同模块设计的具体细节」
使用switch判断语句,但是这种框架的劣性在于如果增加一个模块需要进行大范围修改。
使用function和map数据结构相结合。
接下来我们重点介绍第二种框架
本着说明问题,其他一切从简的原则 我们可以这样设计
#include
#include
#include
using namespace std;
void borrow()
{
std::cout << "borrow books" << std::endl;
}
void lend()
{
std::cout << "Return the book" << std::endl;
}
void search()
{
std::cout << "Search book" << std::endl;
}
int main()
{
using func_t = std::function<void()>;
std::map<int, func_t> Map;
Map.insert({ 1,borrow });
Map.insert({ 2,lend });
Map.insert({ 3,search });
while (1)
{
int option = 0;
std::cout << "请选择:";
std::cin >> option;
auto it = Map.find(option);
if (it == Map.end())
{
cout << "没有这项服务,请重新选择:" << endl;
}
else
{
it->second();
}
}
}
#include
#include
template<class R,class A>
class myfunction<R(A)>
{
public:
using PFUNC = R(*)A;
myfunction(PFUNC pfunc)
:_pfunc(pfunc)
{}
R operator()(A ter)
{
return _pfunc(str);
}
priavte:
PFUNC _pfunc;
};
void hello(std::string str)
{
std::cout << str << std::endl;
}
int main()
{
myfunction<void(std::string)> func_t = hello;
func_t("hello world");
}
但是如果函数签名发生变化,我们就得写不同的部分特例化模板,其实我们可以这样做:
template<class T,class... A1>
class my_function<T(A1...)>
{
public:
using func_t = T(*)(A1...);
my_function(func_t func)
:func_(func)
{}
R operator()(A1... args)
{
return func_(args...);
}
private:
func_t func_;
};
std::bind 是 C++11 引入的一个标准库函数,它位于 functional 头文件中。std::bind 可以用来绑定函数的参数,或者将成员函数和对象绑定在一起,生成一个新的可调用对象(也称为函数对象)。这个新生成的对象可以像普通函数一样被调用,但其内部实际上会调用我们最初绑定的那个函数或成员函数。
#include // 包含 std::bind 的头文件
auto bound_function = std::bind(function, arg1, arg2, ..., argN);
function 是要绑定的函数或可调用对象。
arg1, arg2, …, argN 是传递给 function 的参数,可以是具体的值,也可以是占位符 _1, _2, …(这些占位符定义在
头文件中,通常通过 std::placeholders::_1 等方式访问)。
因此使用bind时往往要包含两个头文件
#include // 包含 std::bind 的头文件
#include // 包含占位符 _1 _2
示例如下
绑定普通函数
#include
#include
void print_sum(int a, int b) {
std::cout << "Sum: " << a + b << std::endl;
}
int main() {
auto bound_print_sum = std::bind(print_sum, 5, 10);
bound_print_sum(); // 输出: Sum: 15
return 0;
}
使用占位符
#include
#include
#include // 包含 std::placeholders 的头文件
void print_sum(int a, int b) {
std::cout << "Sum: " << a + b << std::endl;
}
int main() {
using namespace std::placeholders;
auto bound_print_sum = std::bind(print_sum, _1, 10);
bound_print_sum(5); // 输出: Sum: 15
bound_print_sum(20); // 输出: Sum: 30
return 0;
}
绑定成员函数
#include
#include
#include
class MyClass {
public:
void print_sum(int a, int b) {
std::cout << "Sum: " << a + b << std::endl;
}
};
int main() {
MyClass obj;
using namespace std::placeholders;
auto bound_print_sum = std::bind(&MyClass::print_sum, &obj, _1, 10);
bound_print_sum(5); // 输出: Sum: 15
return 0;
}
注意,在绑定成员函数时,第一个参数需要是对象的指针或引用。
绑定 lambda 表达式
#include
#include
#include
int main() {
auto lambda = [](int a, int b) { std::cout << "Sum: " << a + b << std::endl; };
auto bound_lambda = std::bind(lambda, _1, 20);
bound_lambda(10); // 输出: Sum: 30
return 0;
}
注意事项
何为线程池?
线程池是一种高效的设计方案。我们事先准备若干个线程,然后给不同的线程分配不同的任务。
#include
#include
#include
#include
#include
using namespace std;
using namespace std::placeholders;
class Thread
{
public:
Thread(function<void()>Way)
:_Way(Way)
{}
thread start()
{
thread t(_Way);
return t;
}
private:
function<void()> _Way;
};
class ThreadPoll
{
public:
ThreadPoll()
{}
~ThreadPoll()
{
for (auto it : _poll)
{
delete it;
}
}
void start(int size)
{
for (int i = 0; i < size; i++)
{
_poll.push_back(new Thread(std::bind(&ThreadPoll::ThreadWay, this, i)));
}
for (auto it : _poll)
{
_hander.push_back(it->start());
}
for (auto &it : _hander)
{
it.join();
}
}
private:
void ThreadWay(int i)
{
cout << "thread %d run........" << i << endl;
}
vector<Thread*> _poll;
vector<thread> _hander;
};
int main()
{
ThreadPoll it;
it.start(20);
}
``