C++的函数符概念
函数符也称为函数对象,或伪函数,是将函数或者类对象当作实参使用
其中,将类实例对象当作函数对象使用的,称为类函数符
类函数符是通过重载"()"操作符实现的
两者使用方法:
#include <list>
bool tooBig(int n) { return n > 100; }
class toobig {
public:
bool operator()(int n) { return n > 100; }
};
int main() {
std::list<int> mylist;
mylist.remove_if(tooBig);
toobig tb;
mylist.remove_if(tb);
}
效果完全一致,类操作符的好处是,可以通过私有变量实现算法定制,例如toobig类,增加一个base对象,重载函数修改为"return n>base;",就能够自定义toobig的基准。
C++ STL提供了函数符概念,主要有三种(五种)类型:
- 生成器:无需参数就可以调用
- 一元函数:需要一个参数。如果返回值为bool类型,则称为谓词
- 二元函数:需要两个参数。如果返回值为bool类型,则称为二元谓词
主要应用在非成员算法函数和某些STL类的部分成员函数中,例如list类的remove_if方法,可以接收函数符来决定删除哪些对象
又比如sort方法,其第三个参数就是函数符,用来指定排序条件
C++预置了一些函数符,存放在functional头文件中:
运算符 | 函数符 |
+ | plus |
- | minus |
* | multiplies |
/ | divides |
% | modulus |
- | negate |
== | equal_to |
!= | not_eqaul_to |
> | greater |
< | less |
>= | greater_equal |
<= | less_eqaul |
&& | logical_and |
|| | logical_or |
! | logical_not |
基本覆盖了常用数学和逻辑运算,代码类似:
template<class _Ty = void>
struct divides
{ // functor for operator/
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty result_type;
constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator/ to operands
return (_Left / _Right);
}
};
此外还提供了两个参数绑定类及同功能的绑定方法:binder1st类、binder2nd类、bind1st方法、bind2nd方法,用来实现一元函数和二元函数之间的转换,例如divides函数符,需要接收两个参数,如果想固定除数(_Left)为100,就可以使用(以int为例):
bind2nd(100.divides<int>()) 来代替divides,例如:
transform(mylist.begin(),mylist.end(),iter,bind2nd(100,divides<double>()));
之所以使用函数符而不是函数指针,原因是函数指针需要指定返回类型,而STL是模板类,使用了泛型,如果要用函数指针,就必须为每种类型都适配一个实现,非常麻烦。
实际上STL在应用函数符的时候,底层也是靠迭代器进行的,也就是类型无关,例如remove_if方法:
template<class _Pr1>
void remove_if(_Pr1 _Pred)
{ // erase each element satisfying _Pred
_Remove_if(_Pred);
}
template<class _Pr1>
void _Remove_if(_Pr1& _Pred)
{ // erase each element satisfying _Pred
for (iterator _First = begin(); _First != end(); )
if (_Pred(*_First))
_First = erase(_First);
else
++_First;
}
Java的类似实现
Java 8也提供了将函数实例对象当作参数,调用指定方法执行操作的功能,相关接口:
Predicate<T>、Consumer<T>、Function<T>、Supplier<T>、Operator<T>
全部在java.util.function包中,包里还包含了很多(没有任何意义的)各种类型的继承接口,比如IntPredicate
Collection派生类和Map派生类都提供了如foreach、removeIf等方法使用函数符,例如:
import java.util.ArrayList;
public class Solution {
public static void main(String args[]) {
ArrayList<Integer> mylist=new ArrayList<>();
mylist.removeIf(integer -> integer>100);
}
}
和C++部分第一段示例代码功能一样
但是Java内部使用的是for循环实现而非迭代器
另外Java由于没有操作符重载,也没有面向过程式编程风格,只能将函数符划分为五个接口实现,如果不是lambda形式的话,使用起来还是比较麻烦的,这一点不如C++,个人觉得完全可以使用Method类,通过反射实现