一、可调用对象
在C++中存在“可调用对象”这么一个概念。
1、函数指针
普通函数指针方式:
#include <iostream>
using namespace std;
void func(int a) {
cout << "func pointer " << a << endl;
}
int main() {
void (*ptr)(int) = func; // 函数指针
void(&ref)(int) = func; // 函数引用 c++11
ptr(1); // 或者写成(*ptr)(1);
ref(2); // 指针和引用调用方式相同
int(*ptr_lambda)(int) = [](int b) { // lamdba表达式
cout << "lambda function" << endl;
return b;
};
cout << "lambda " << ptr_lambda(3) << endl;
return 0;
}
执行结果:
func pointer 1
func pointer 2
lambda function
lambda 3
using方式:
#include <iostream>
using namespace std;
void func(int a) {
cout << "func pointer " << a << endl;
}
int main() {
using ptr = void(*)(int);
ptr p = func; // 或写为ptr p = &func;
(*p)(1); // 或者写为(*p)(1);
return 0;
}
执行结果:
func pointer 1
2、仿函数
函数指针不支持指向仿函数!!!
#include <iostream>
using namespace std;
class Myclass {
public:
void operator()() {
cout << "operator()" << endl;
}
};
int main() {
Myclass myclass;
myclass();
return 0;
}
执行结果:
operator()
3、可被转换为函数指针的类对象
#include <iostream>
using func_ptr = void(*)(int);
class MyClass {
public:
// 示例成员函数
static void print(int value) {
std::cout << "Value: " << value << std::endl;
}
// 转换运算符,将类对象转换为函数指针
operator func_ptr() {
return print;
}
};
int main() {
MyClass myObject;
// 使用转换运算符获取成员函数指针
void (*functionPointer)(int) = myObject;
// 使用函数指针调用成员函数
functionPointer(42);
return 0;
}
chatGPT版本:
#include <iostream>
class MyClass {
public:
// 示例成员函数
void print(int value) {
std::cout << "Value: " << value << std::endl;
}
// 转换运算符,将类对象转换为函数指针
operator void(*)(int)() {
return &MyClass::print;
}
};
int main() {
MyClass myObject;
// 使用转换运算符获取成员函数指针
void (*functionPointer)(int) = myObject;
// 使用函数指针调用成员函数
functionPointer(42);
return 0;
}
4、类成员函数指针、者类成员指针及类的成员指针(变量)
指向类成员函数时,需要加上"类名::"区分普通函数和类成员函数。
普通函数指针方式:
#include <iostream>
using namespace std;
class Base {
public:
int m_a;
int m_b;
public:
Base(int a, int b) : m_a(a), m_b(b) {}
void display() {
cout << "Base display" << endl;
}
static void count() {
cout << "Base Count" << endl;
}
};
int main() {
// 类成员函数
void (Base:: * ptr_display)() = &Base::display; // 必须加'&'符号
Base b(1, 2);
(b.*ptr_display)(); // 调用时需要绑定对象
// 类静态函数
void (*ptr_count)() = &Base::count; // 可替换为Base::count,无需加'&'符号
ptr_count(); // 调用时无需绑定对象
// 类成员指针指向类成员变量
int Base::* obj_ptr = &Base::m_a;
(b.*obj_ptr) = 10;
cout << "number is: " << b.m_a << endl;
return 0;
}
using方式:
#include <iostream>
using namespace std;
class Base {
public:
int m_a;
int m_b;
public:
Base(int a, int b) : m_a(a), m_b(b) {}
void display() {
cout << "Base display" << endl;
}
static void count() {
cout << "Base Count" << endl;
}
};
int main() {
// 类成员函数
using ptr = void (Base::*)();
ptr ptr_display = &Base::display; // 必须加'&'符号
Base b(1, 2);
(b.*ptr_display)(); // 调用时需要绑定对象
// 类静态函数
using ptr_static = void (*)();
ptr_static ptr_count = &Base::count; // 可替换为Base::count,无需加'&'符号
ptr_count(); // 调用时无需绑定对象
// 类成员指针指向类成员变量
using obj_ptr = int Base::*;
obj_ptr p = &Base::m_a;
b.*p = 10;
cout << "number is: " << b.m_a << endl;
return 0;
}
执行结果:
Base display
Base Count
number is: 10
二、可调用对象包装器std::function
在C++中,std::function
是一个可调用对象的包装器,允许您将各种可调用实体(函数、函数指针、成员函数指针、Lambda 表达式等)存储在一个对象中,然后通过该对象进行调用。
注意:
-
std::function必须要包含一个叫做functional的头文件;
-
类的成员函数不能直接通过std::function进行打包,它还需要std::bind进行协助
格式:
#include <functional>
std::function<返回值类型(参数类型列表)> 对象名称 = 可调用对象;
1、常见用法
(1)包装普通函数
#include <iostream>
#include <functional>
using namespace std;
void func(int a) {
cout << "func pointer " << a << endl;
}
int main() {
// 普通函数
function<void(int)> f1 = func;
f1(5);
// lamdba
function<void(void)> f2 = []() {
cout << "lamdba" << endl;
};
f2();
return 0;
}
执行结果:
func pointer 5
lamdba
(2)包装类的静态函数
#include <iostream>
#include <functional>
using namespace std;
class Base {
public:
static void count() {
cout << "Base Count" << endl;
}
};
int main() {
// 类静态成员函数
Base b;
function<void(void)> f1 = Base::count;
f1();
return 0;
}
执行结果:
Base Count
(3)包装仿函数
#include <iostream>
#include <functional>
using namespace std;
class Func {
public:
void operator()(int a) {
cout << "operator: " << a << endl;
}
};
int main() {
// 仿函数
Func func;
function<void(int)> f1 = func;
f1(5);
return 0;
}
执行结果:
operator: 5
2、回调函数
使用对象包装器取代函数指针进行回调函数。
#include <iostream>
#include <functional>
using namespace std;
class Myclass
{
public:
// 构造函数参数是一个包装器对象
Myclass(const function<void()>& f) : callback(f)
{
}
void print()
{
callback(); // 调用通过构造函数得到的函数指针
}
private:
function<void()> callback;
};
// 仿函数
class A
{
public:
void operator()() {
cout << "仿函数示例" << endl;
}
static void hello() {
cout << "类静态成员函数" << endl;
}
};
// 普通函数
void func() {
cout << "普通函数示例" << endl;
}
int main(void)
{
// 仿函数通过包装器对象进行包装
A a;
Myclass myclass1(a);
myclass1.print();
// 普通函数
Myclass myclass2(func);
myclass2.print();
Myclass myclass3(A::hello);
myclass3.print();
return 0;
}
执行结果:
仿函数示例
普通函数示例
类静态成员函数
三、绑定器std::bind
std::bind
是 C++ 标准库中 <functional>
头文件提供的一个函数模板。它用于将参数绑定到一个函数,创建一个表示对原始函数调用的函数对象。在需要将特定参数固定或重新排列的情况下,std::bind
可以很有用,特别是在处理算法或回调函数等场景时。
std::bind主要作用:
- 将可调用对象与其参数一起绑定成一个仿函数;
- 将多元(参数个数为n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。
std::bind语法:
// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
1、绑定器绑定参数得到仿函数
#include <iostream>
#include <iostream>
#include <functional>
using namespace std;
void print(int x, int y)
{
cout << x << " " << y << endl;
}
int main(void)
{
// 使用绑定器绑定可调用对象和参数并得到仿函数
bind(print, 3, 4)();
bind(print, placeholders::_1, 2)(5);
bind(print, 2, placeholders::_1)(5);
// bind(print, 2, placeholders::_2)(5); // 错误, 参数列表中没有第二个参数,placeholders::_2与参数列表无法对应
bind(print, 2, placeholders::_2)(3, 4); // 调用时第一个参数3不会被使用
bind(print, placeholders::_1, placeholders::_2)(7, 8); // 占位符与参数列表要相对应
bind(print, placeholders::_2, placeholders::_1)(7, 8);
return 0;
}
执行结果:
3 4
5 2
2 5
2 4
7 8
8 7
placeholders::_1是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5等依次类推…
2、std::function和std::bind结合使用的例子
std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,我们不需要关心绑定器的返回值类型,可以方便的使用auto进行自动类型推导。
#include <iostream>
#include <functional>
using namespace std;
void callFunc(int x, const function<void(int)>& f) {
f(x);
}
void print(int x)
{
cout << "x = " << x << endl;
}
void add100(int x)
{
cout << "x +100 = " << x + 100 << " ";
}
int main(void)
{
// 使用绑定器绑定可调用对象和参数
auto f1 = bind(print, placeholders::_1);
callFunc(10, f1);
auto f2 = bind(add100, placeholders::_1);
callFunc(10, f2);
return 0;
}
执行结果:
x = 10
x +100 = 110
2、std::bind实现类成员函数指针、类成员指针的包装
可调用对象包装器std::function是不能实现对类成员函数指针或者类成员指针的包装的,但是通过绑定器std::bind的配合之后,就可以解决这个问题了。
#include <iostream>
#include <functional>
using namespace std;
class Myclass
{
public:
void print(int x, int y)
{
cout << "x: " << x << ", y: " << y << endl;
}
int m_a = 100;
};
int main(void)
{
Myclass c;
// 绑定类成员函数
function<void(int, int)> f1 = bind(&Myclass::print, &c, placeholders::_1, placeholders::_2);
// 绑定类成员变量(公共)
function<int& (void)> f2 = bind(&Myclass::m_a, &c); // 类成员需要可读可写,因此使用引用&(否则不可改变变量值),int代表m_a的类型,指定void则是因为可调用类成员变量没有参数
f1(1, 2);
f2() = 10;
cout << "t.m_a: " << c.m_a << endl;
return 0;
}
执行结果:
x: 1, y: 2
t.m_a: 10