在本文中,我想先详细讲解C++中的可调用对象和函数对象的定义,最后讲解二者的联系和区别
可调用对象
C++中,可调用对象(Callable Object)是一个可以被调用执行的对象,类似于函数。任何类型的对象,主要的可调用对象有以下几类:
1. 函数指针:指向函数的指针可以被调用。
2. 普通函数:直接定义的函数可以直接调用。
3. Lambda 表达式(本质是编译器为我们构造函数对象):C++11引入的Lambda允许创建匿名函数,它们是可调用的,其实在C++中,当我们创建一个lambda表达式,本质上是创建了一个未命名的类,而且这类是函数对象。
4. 函数对象(Functor):也叫仿函数,是一个行为类似函数的对象。这通常是通过在类中重载operator()
来实现的,使得该类的实例可以像函数那样被调用。
5. 成员函数指针:指向类成员函数的指针,需要与一个对象实例一起使用。
**6. std::function:**是一个泛型函数包装器,可以存储、复制和调用任何类型的可调用对象,包括函数指针、Lambda表达式、函数对象等。
7. 绑定表达式:std::bind 创建的绑定表达式可以存储一个可调用对象及其参数,并在之后被调用。
函数指针
#include <iostream>
using namespace std;
void hello() {
cout << "Hello, world!" << endl;
}
int main() {
void (*funcPtr)() = hello; // 函数指针
funcPtr(); // 通过函数指针调用函数
return 0;
}
Lambda表达式
具体关于Lambda表达式的内容可见:【C++语法】一起探讨 lambda 表达式!
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
int multiplier = 3;
for_each(v.begin(), v.end(),
[multiplier](int n) { cout << n * multiplier << endl; });
return 0;
}
函数对象
通常是通过在类中重载operator()
来实现的,使得该类的实例可以像函数那样被调用
#include <iostream>
using namespace std;
class Multiply {
public:
Multiply(int y) : multiplier(y) {}
int operator()(int x) const {
return x * multiplier;
}
private:
int multiplier;
};
int main() {
Multiply multiplyBy2(2);
cout << "2 multiplied by 2 is " << multiplyBy2(2) << endl;
return 0;
}
成员函数指针
指向类成员函数的指针,需要与一个对象实例一起使用。
#include <iostream>
using namespace std;
class Greeter {
public:
void greet() {
cout << "Hello from Greeter!" << endl;
}
};
int main() {
Greeter greeter;
void (Greeter::*funcPtr)() = &Greeter::greet; // 成员函数指针
(greeter.*funcPtr)(); // 调用成员函数
return 0;
}
std::function
#include <iostream>
#include <functional>
using namespace std;
void printSum(int a, int b) {
cout << "Sum: " << a + b << endl;
}
int main() {
function<void(int, int)> func = printSum; // function 可以存储普通函数
func(5, 3); // 使用 function 调用函数
return 0;
}
函数对象
在C++中,函数对象(通常被称为functor
)是指任何可以通过函数调用运算符()
被调用的对象。具体而言,这意味着在类中实现了重载的operator()
成员函数的任何对象都可以被视为函数对象。这使得对象在语法上表现得像一个普通函数。
函数对象的主要优点是它们比普通函数更灵活,因为它们可以拥有状态。通过在类内部保持状态,函数对象可以在多次调用之间保持信息。此外,它们还可以利用面向对象的特性,如封装和继承。
函数对象在C++标准库中广泛使用,尤其是在算法库中,如std::sort或std::for_each等算法可以接受自定义的函数对象作为参数,以定义特定的行为。此外,函数对象也常用于实现回调和绑定事件。
典型例子
#include <iostream>
class Adder {
public:
// 构造函数,可选地初始化加数
Adder(int initValue = 0) : value(initValue) {}
// 重载函数调用运算符
int operator()(int x) {
return x + value;
}
private:
int value;
};
int main() {
Adder addFive(5); // 创建一个Adder对象,初始值为5
std::cout << "Result: " << addFive(3) << std::endl; // 输出8,因为 3 + 5 = 8
return 0;
}