28. 可调用对象
一、什么是可调用对象
在 C++ 中,可调用对象(Callable Object)是指可以作为函数调用的对象。C++ 中有多种类型的可调用对象,包括:
- 函数:这是最直接的调用对象,可以是普通函数或成员函数。
- 函数指针:指向函数的指针也可以作为可调用对象。
- Lambda 表达式:C++11 引入了 Lambda 表达式,它允许你定义匿名函数。
- 仿函数(Functors):这是重载了
()
操作符的类的对象,使得它们可以像函数一样被调用。 - 成员函数指针:指向类成员函数的指针。
- 绑定器(Binders):如
std::bind
函数返回的对象,可以绑定函数和参数,创建一个新的可调用对象。 - 包装器(Call wrappers):如
std::function
,它可以包装任何可调用对象,使其具有统一的调用接口。 - 协程 (C++20引入):允许函数在保持状态的情况下暂停和恢复执行的特殊函数。
可调用对象的使用极大地增强了 C++ 的灵活性和表达能力,特别是在编写模板代码和回调函数时。通过使用 std::function
和 std::bind
,可以创建更为灵活的函数对象,允许在运行时选择和组合不同的行为。此外,它们在编写并发代码和响应式编程时也非常有用,因为它们可以轻松地传递和处理异步操作。
【1】、函数指针
#include <iostream>
using namespace std;
int max(int a, int b);
int main(void)
{
int num1 = 0,num2 = 0;
int maxNum = 0;
/**
* 定义函数指针
* 函数指针的名字是 pmax
* int 表示该函数指针指向的函数是返回int类型的
* (int,int)表示该函数指针指向的函数形参是接收两个int
* 在定义函数指针时,也可以写上形参名int (*pmax)(int a,int b) = &max;
* 对于函数来说,&函数名和函数名都是函数的地址
*/
int (*pmax)(int,int) = max;
cout << "请输入两个数,中间以空格分隔:" << endl;
cin >> num1;
cin >> num2;
/**
* (*pmax)(num1,num2)通过函数指针去调用函数
* 也可以这样调用pmax(num1,num2);
*/
maxNum = (*pmax)(num1,num2);
cout << "the max num is : " << maxNum;
return 0;
}
int max(int a, int b)
{
return a>b ? a : b;
}
【2】、重载 operator() 成员函数的类对象
#include <iostream>
using namespace std;
class Person
{
public:
// 属性的声明
string name;
int age;
public:
// 构建函数
Person(void);
Person(string name, int age);
// 运算符重载
void operator()(void);
};
// 无参构造函数
Person::Person(void){}
// 有参构造函数,使用初始化列表进行初始化
Person::Person(string name, int age) : name(name), age(age){}
// 函数调用运算符重载
void Person::operator()(void)
{
cout << "{name: " << this->name << ", age: " << this->age << "}\n";
}
int main(void)
{
Person p("Sakura", 10);
p();
return 0;
}
【3】、指向类成员函数的指针
#include <iostream>
using namespace std;
class Person
{
public:
// 属性的声明
string name;
int age;
public:
// 构建函数
Person(void);
Person(string name, int age);
void say(string message);
};
// 无参构造函数
Person::Person(void){}
// 有参构造函数,使用初始化列表进行初始化
Person::Person(string name, int age) : name(name), age(age){}
void Person::say(string message)
{
cout << this->name << " say: " << message << endl;
}
int main(void)
{
Person p("Sakura", 10);
// 指向类成员函数的指针
void (Person::*ptr)(string) = Person::say;
(p.*ptr)("hello world");
return 0;
}
二、可调用对象包装器
在 C++ 中,可调用对象包装器(Callable Wrapper)是一种设计模式,用于将可调用对象(如函数、函数对象、Lambda 表达式、成员函数指针等)包装起来,以便于存储、传递和调用。这种模式在回调机制、事件处理、任务调度等场景中非常有用。C++ 11 及更高版本的 C++ 标准库提供了一些机制来支持可调用对象的包装和存储,例如 std::function
。
std::function
是一个可调用对象的包装器,它可以存储、复制和调用任何可调用对象,只要它们的调用签名相同。它是一种泛化的函数指针,可以指向任何类型的函数或可调用对象。
【1】、包装普通函数
#include <iostream>
#include <functional>
using namespace std;
int fmax(int a, int b);
int main(void)
{
int num1 = 0,num2 = 0;
int maxNum = 0;
// 包装普通函数
function<int(int,int)> pmax = fmax;
cout << "请输入两个数,中间以空格分隔:" << endl;
cin >> num1;
cin >> num2;
// 调用函数
maxNum = pmax(num1,num2);
cout << "the max num is : " << maxNum;
return 0;
}
int fmax(int a, int b)
{
return a>b ? a : b;
}
【2】、包装仿函数
#include <iostream>
#include <functional>
using namespace std;
class Person
{
public:
// 属性的声明
string name;
int age;
public:
// 构建函数
Person(void);
Person(string name, int age);
// 运算符重载
void operator()(void);
};
// 无参构造函数
Person::Person(void){}
// 有参构造函数,使用初始化列表进行初始化
Person::Person(string name, int age) : name(name), age(age){}
// 函数调用运算符重载
void Person::operator()(void)
{
cout << "{name: " << this->name << ", age: " << this->age << "}\n";
}
int main(void)
{
Person p("Sakura", 10);
// 包装仿函数
function<void(void)> f = p;
f();
return 0;
}
三、可调用对象绑定器
在 C++ 中,std::bind
是一个功能强大的工具,它可以将可调用对象与其参数绑定在一起,创建出一个新的可调用对象。这个新的可调用对象可以带有预绑定的参数,或者改变了参数的顺序,或者两者都有。std::bind
主要用于函数式编程和回调函数中,它可以在不修改原有函数的情况下,灵活地调整函数的参数列表。
std::bind
可以用来绑定函数和参数,生成一个新的可调用对象。std::bind
使用 std::placeholders
命名空间中的占位符来表示绑定过程中的参数位置。_1
, _2
, _3
, … 分别表示第一个、第二个、第三个参数,依此类推。
【1】、绑定普通函数
#include <iostream>
#include <functional>
using namespace std;
int fmax(int a, int b);
int main(void)
{
int num1 = 0,num2 = 0;
cout << "请输入两个数,中间以空格分隔:" << endl;
cin >> num1;
cin >> num2;
// 使用绑定器绑定可调用对象和参数,并调用的到相应的仿函数
auto pmax = bind(fmax, num1, num2);
int maxNum = pmax();
cout << "the max num is : " << maxNum;
return 0;
}
int fmax(int a, int b)
{
return a>b ? a : b;
}
我们可以使用 std::placeholders
命名空间中的占位符来表示绑定过程中的参数位置。
#include <iostream>
#include <functional>
using namespace std;
int fmax(int a, int b);
int main(void)
{
int num1 = 0,num2 = 0;
cout << "请输入两个数,中间以空格分隔:" << endl;
cin >> num1;
cin >> num2;
// 使用绑定器绑定可调用对象和参数,并调用的到相应的仿函数
auto pmax = bind(fmax, placeholders::_1, placeholders::_2);
int maxNum = pmax(num1, num2);
cout << "the max num is : " << maxNum;
return 0;
}
int fmax(int a, int b)
{
return a>b ? a : b;
}
// 使用绑定器绑定可调用对象和参数,并调用的到相应的仿函数
maxNum = bind(fmax, num1, num2)();
// 绑定参数,使用占位符 placeholders::_x表示第x个参数
maxNum = bind(fmax, placeholders::_1, placeholders::_2)(num1, num2);
maxNum = bind(fmax, placeholders::_1, num2)(num1);
maxNum = bind(fmax, num1, placeholders::_1)(num2);
// 调用时,第一个参数使用绑定的数值,不会使用传递进来的数值
maxNum = bind(fmax, num1, placeholders::_2)(num2, num2);
【2】、绑定类的成员函数
#include <iostream>
#include <functional>
using namespace std;
class Person
{
public:
// 属性的声明
string name;
int age;
public:
// 构建函数
Person(void);
Person(string name, int age);
void say(string message);
};
// 无参构造函数
Person::Person(void){}
// 有参构造函数,使用初始化列表进行初始化
Person::Person(string name, int age) : name(name), age(age){}
void Person::say(string message)
{
cout << this->name << " say: " << message << endl;
}
int main(void)
{
Person p("Sakura", 10);
// 指向类成员函数的指针
bind(Person::say, &p, placeholders::_1)("hello world");
return 0;
}
并且,可调用对象的包装器和可调用对象的绑定器可以结合起来使用。
// 指向类成员函数的指针
function<void(string)> f = bind(Person::say, &p, placeholders::_1);
f("Hello World!");
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通