
所谓对象,就是在内存中存在的具体实例。
那可调用对象,就是可以像函数一样调用的对象。
- 函数与函数指针 (继承自C)
- 仿函数
Lambda表达式
最基本的可以调用的对象就是函数,而指向某个函数的函数指针,也是可调用的
仿函数 什么是仿函数?首先仿函数是一个自定义的class实例, 然后这个实例可以指定参数调用自己,实现像函数一样的行为
调用自己,指的是可以以圆括号加参数的形式, 像函数一行执行, 执行的函数就是这个示例对应的圆括号函数重载. 如:
// Less类型实现了圆括号的 *** 作符重载,参数为int a, int b, 实现的功能是判断a是否小于b
class Less {
public:
bool operator()(int a, int b)
{
return a < b;
}
};
Less less; // 定义一个Less对象实例less, 则less实例就是仿函数
std::cout << less(0, 1) << std::endl; // 0 < 1, 输出1, 对应true
std::cout << less(100, 50) << std::endl; // 100不小于50, 对应false
std::cout << less(10, 10) << std::endl; // 10不小于10, 对应false
为什么要用仿函数?
疑问1: 已经有了函数指针了,为什么还需要仿函数?
- 首先函数指针只能指向一般的函数,并且除了函数指定的参数外,没有办法传递多余的参数,使用场景有限
- 函数指针属于运行时动态函数,调用前需要确保指针合法,仿函数一般不需要额外判断,且更安全
- 函数指针调用时,需要对指针解引用,与仿函数相比,效率更低,仿函数属于编译期函数跳转
- 仿函数除了调用参数外,可以通过类构造函数传入额外的参数,这一点函数指针做不到,比如:
// 有一个外部函数, 用来过滤数据
template<Func>
std::vector<int> filter(Func&& func, const std::vector<int>& src);
// 假设需要筛选某个范围的值
std::vector<int> numbers{1, 4, 5, 2, 7, 8, 0};
// 使用仿函数
class Between {
public:
Between(int down, int up): down_(down), up_(up) {}
bool operator(int number) const
{
return down_ <= number && number <= up;
}
private:
int down_;
int up_;
};
// 筛选[1, 5]区间的整数并且筛选范围还可以使用变量来控制, 仿函数可以轻松实现,但是函数指针不行
std::vector<int> filtered = filter(Between(1, 5), numbers); // 1, 4, 5, 2
疑问2: 已经有各种原生函数了,为什么还要仿函数?
原生的函数,只能使用固定的参数,定义的形式确定后,调用的形式也是固定的,无法指定多余的参数, 所以在传统C时代,都会使用一个额外的void*参数传递额外参数,且类型需要开发者自己保证安全
这是一个可以将同一调用规则的可调用对象使用统一类型封装的一个盒子,只要可调用对象的形式符合这个盒子的要求,就都可以放到这个盒子里,并且这个盒子还可以什么也不放(此时不可调用)
可以统一类型有什么好处?- 可以像函数指针一样动态绑定可调用对象,比如绑定指定函数,绑定自定义仿函数等等
- 可以容器化管理,比如某个业务需要职责链模式,那这个职责链里面的所有可调用对象就可以通过
functional放到列表或者映射中
-
可以简单的包装普通函数,类静态函数,与原函数用法一样
int funcA(); std::function<void()> func = std::bind(funcA); func(); // 与直接调用funcA()效果一样 -
可以将对象和成员函数绑定在一起,最为一个新的可调用对象,调用对象的参数形式与成员函数的参数保持一致
class Bird { public: void fly() { // ... } }; Bird some_bird; Bird another_bird; std::function<void()> bird1_fly = std::bind(&Bird::fly, &some_bird); std::function<void()> bird2_fly = std::bind(&Bird::fly, &another_bird); bird1_fly(); // 转换成了普通函数调用的形式,并且内部隐含了固定bird对象作为内部参数 bird2_fly(); -
除了可以将现有的成员函数变换形式, 也能将其他函数表换形式,适配目标调用形式
lambda表达式// 可调用目标的形式 std::function<bool(int)> filter_item; // 我们有如下几个函数 bool less(int a, int b); // a < b bool is_times(int a, int b); // a % b == 0 // 除了自定义仿函数将这几个函数再封装一层以外,我们可以使用bind来封装 // 1. bind less函数,并且a参数固定为100,b作为过滤参数, 过滤大于100的数 filter_item = std::bind(less, 100); filter_item(10); // => 100 < 10 ? false, 100不小于10 filter_item(1000); // => 100 < 1000 ? true, 100小于1000 // 2. bind less函数,并且参数b固定100, a作为参数,过滤小于100的数 filter_item = std::bind(less, std::placeholders::_1, 100); filter_item(10); // => 10 < 100 ? true, 10小于100 filter_item(1000); // => 1000 < 100 ? false, 1000不小于100 // 3. 是否4的倍数 filter_item = std::bind(is_times, std::placeholders::_1, 4); filter_item(8); // true filter_item(3); // false // 4. 是否1000的因数 filter_item = std::bind(is_times, 1000); filter_item(10); // true filter_item(100); // true filter_item(101); // false
匿名仿函数类,功能与仿函数一致, 但是可以直接使用代码块生成一个仿函数, 并且代码块也可以绑定函数参数以外的变量作为额外的调用参数,绑定外部变量不外部值绑定(拷贝一份到仿函数内部)和引用绑定(将外部对象的引用传入仿函数内部)
int upper = 100;
std::function<bool(int)> filter = [upper](int number) {
return number < upper;
};
upper = 50; // 重新绑定
filter = [upper](int number) {
return number < upper;
};
// 直接绑定引用
filter = [&upper](int number) {
return number < upper;
};
upper = 10;
filter(1); // 1 < 10 ? true
upper = 1;
filter(100); // 100 < 1 ? false
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)