函数对象(仿函数)
1. 什么是函数对象,什么是仿函数
在 C++ 中,函数对象(也称为仿函数)是一个行为类似函数的对象。任何类如果重载了 operator()
,那么该类的对象就可以作为函数来使用,这样的对象就被称为函数对象。
函数对象的优点包括:
-
内联优化:由于函数对象是类的对象,其操作符重载函数通常会被编译器内联,这可以消除函数调用的开销,提高性能。
-
状态保持:函数对象可以有自己的成员变量,这意味着它们可以保持状态。这对于需要在多次调用之间保持状态的操作非常有用。
-
灵活性:函数对象可以被参数化。例如,你可以在构造函数中传入参数来改变函数对象的行为。这种灵活性使得函数对象可以很容易地适应不同的需求。
以下是一个简单的函数对象的例子:
class Add {
public:
Add(int x) : x_(x) {}
int operator()(int y) const {
return x_ + y;
}
private:
int x_;
};
int main() {
Add add_five(5);
std::cout << add_five(6); // 输出:11
return 0;
}
在这个例子中,Add
是一个函数对象,它接受一个参数 x
,然后返回 x + y
。我们可以像调用函数一样调用 add_five(6)
,并得到结果 11
。
2. stl中的函数对象有哪些?
STL(Standard Template Library,标准模板库)中包含了一些预定义的函数对象,这些函数对象主要分为两类:算术运算函数对象和关系运算函数对象。
算术运算函数对象:
std::plus
:加法函数对象std::minus
:减法函数对象std::multiplies
:乘法函数对象std::divides
:除法函数对象std::modulus
:取模函数对象std::negate
:取反函数对象
关系运算函数对象:
std::equal_to
:等于函数对象std::not_equal_to
:不等于函数对象std::greater
:大于函数对象std::less
:小于函数对象std::greater_equal
:大于等于函数对象std::less_equal
:小于等于函数对象
这些函数对象都是模板,可以用于任何类型,只要该类型支持对应的运算符。例如,std::less<int>
是一个可以比较两个整数大小的函数对象。
2.1 stl的函数对象怎么用?
std::less
是一个函数对象,它提供了一种方式来比较两个对象。它的行为类似于 <
运算符。以下是一个 std::less
的使用例子:
#include <iostream>
#include <functional>
int main() {
std::less<int> less;
std::cout << std::boolalpha; // 输出 bool 值为 true 或 false 而不是 1 或 0
std::cout << "less(10, 20): " << less(10, 20) << std::endl; // 输出:true
std::cout << "less(20, 10): " << less(20, 10) << std::endl; // 输出:false
std::cout << "less(10, 10): " << less(10, 10) << std::endl; // 输出:false
return 0;
}
在这个例子中,我们创建了一个 std::less<int>
的对象 less
,然后使用它来比较两个整数的大小。less(10, 20)
返回 true
,因为 10 小于 20;less(20, 10)
返回 false
,因为 20 不小于 10;less(10, 10)
返回 false
,因为 10 不小于 10。
99. quiz
1. 函数对象和lambda有什么本质不同吗?两者都可以保存状态变量
函数对象和 lambda 表达式在 C++ 中都可以用来封装行为,并且都可以保存状态。但是,它们在使用方式和适用场景上有一些不同:
-
定义方式:函数对象是通过定义一个类并重载
operator()
来创建的,而 lambda 表达式是通过一个简洁的语法直接在代码中创建的。 -
状态保存:函数对象可以通过成员变量来保存状态,而 lambda 表达式则通过捕获列表来保存状态。函数对象的状态保存更为直观和灵活,而 lambda 表达式的状态保存更为简洁和方便。
-
复用性:函数对象可以在定义后在多处复用,而 lambda 表达式通常在定义后立即使用,并不易于复用。
-
复杂性:对于复杂的行为,使用函数对象可能更为合适,因为你可以利用类的所有特性(如私有成员、成员函数等)来组织你的代码。而对于简单的行为,使用 lambda 表达式可能更为合适,因为它的语法更为简洁。
总的来说,函数对象和 lambda 表达式在某些方面是相似的,但是它们在使用方式和适用场景上有一些不同。你应该根据你的具体需求来选择使用哪一种。
2. 函数对象、lambda、普通函数,性能上有什么不同吗?编译器行为有什么区别?
在大多数情况下,函数对象、lambda 表达式和普通函数的性能是相似的。这是因为现代编译器通常会对这些构造进行优化,例如内联函数调用,以减少函数调用的开销。
以下是这三种构造在编译器行为上的一些区别:
-
函数对象:函数对象是类的对象,其操作符重载函数通常会被编译器内联,这可以消除函数调用的开销,提高性能。此外,函数对象可以有自己的成员变量,这意味着它们可以保持状态。
-
lambda 表达式:lambda 表达式在编译时会被转换为函数对象,因此它们的性能特性和函数对象相似。lambda 表达式的一个优点是它们可以捕获周围的环境,这使得它们在编写闭包时非常有用。
-
普通函数:普通函数在编译时会被转换为机器代码,它们不能保持状态(除非使用静态变量),也不能捕获周围的环境。但是,普通函数可以被声明为内联函数,提示编译器进行内联优化。
总的来说,函数对象、lambda 表达式和普通函数在性能上的差异通常可以忽略不计。你应该根据你的具体需求和编程风格来选择使用哪一种。在考虑性能时,你应该首先关注算法的复杂性,然后再考虑这些低级的优化。