新C++(15):可调用对象及包装
创始人
2025-05-30 11:38:34
0

"彼岸岸上寻找,而天空爱上了飞鸟。"

void (*signal(int signum,void(*handler)(int))))(int);

上述给你一段这样的代码,你能知道这是什么嘛?唔...,似乎有点打脑壳。这是什么鬼??如果你把void后面的一些进行省略成这个的形式: void (*)(int),也许你会脱口而出,这是函数指针!

是的,这是一个经典的信号处理函数。那本章是为了来讲C语言那天马行空般恼人的函数指针??肯定不是,这个任务得去交给学习C语言指针时该去做的。函数指针的本质,就是函数入口的地址,但显然这对我们尤其是初学者特别不友好,这层层嵌套,何时才能嵌套得完?

因此,C++提供了其他的方式来替代传统式函数指针的应用。

---前言

一、什么是可调用对象

可调用对象除开传统的函数指针,C++还提供了其他两种调用对象。

①仿函数
②lambda表达式


二、仿函数

如果我们需要一个函数,实现两个数的加法。

//传统写法 无非就是定义函数
int AddFunc(int x, int y)
{return x + y;
}

但是呢,C++引入了仿函数的语法之后,与普通函数相比较,仿函数的使用更加便利。

所谓仿函数,就是让一个类,像函数一样去使用
struct AddObj
{int operator()(int x, int y){return x + y;}    
};

也许你会说,啊你这仿函数用起来,也不比普通函数调用简单多少,还不如原来普通函数调用呢。是的,对于上述这中情况,普通函数调用和仿函数似乎都很简单。

struct Func2
{int operator()(int base, int n){for (int i = 1;i <= n;++i){base += i;}return base;}
};template
class Num
{
public:int accmulate(int n){return Func()(_base, n);}private:int _base = 0;
};

显然,当面对这种情况,仿函数可以作为参数传递给一个类的模板作参数,仅凭这一点,仿函数的实用性其实就比普通函数广太多了。


三、lambda表达式

C++11等于说"抄了别人语法的作业",但确实lambda表达式的出现是非常成功便捷的。

(1)lambda表达式语法

[capture-list](parameters)mutable->return-type{ statement };

[cpature-list] : 捕捉列表。编译器会根据[]来判断是否是lambda表达式,能够捕获该代码段内的上下文变量供lambda函数体使用。
(parameters):参数列表如果不需要参数传递,则可以连同()一起省略。

->returntype:返回值类型(可以省略不写,由编译器对返回类型进行推导)。

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

{statement}:函数体。该函数体可以使用其参数、以及捕获列表中捕获的对象。

(2)lambda表达式的应用

lambda表达式的语法稍微过了一遍后,我们来看看它的应用吧。

    //1.最简单的lambda表达式//参数没有可以不用写 、 返回值也可以让编译器 推导[] {};//2.捕获上下文变量int a = 3, b = 4;auto f = [=] { return a + b;};cout << f() << endl;

使用=、&捕捉:

我们现在想使用一个lambda表达式 交换两个数的值。

    auto f1 = [](int& a, int& b){int tmp = a;a = b;b = tmp;};//引用或者叫取地址捕捉auto f2 = [&]{int tmp = a;a = b;b = tmp;};

这样我们就能通过写lambda表达式,写一个简易的swap函数。

四、包装器与绑定

如果我们看到这个代码, ret = Func(x)。

这个Func是什么??emm它可能是一个函数调用,可能是一个仿函数,也可能是一个lambda表达式。因为这些都是可调用对象。

我们再来看看下面的一份代码:

template
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}double f(double i)
{return i / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};

我们用三个不同的可调用对象,函数名、仿函数、lambda仿函数作为函数参数模板传入类useF中。

他们的功能如出一辙,但是唯一不同的是,我们在useF类中定义的静态成员变量Count是存在多份的。也就意味着,在useF实例化对象时,实际上是实例化出3份功能相同的函数的。

但是如果我们只想让这个类实例化出一份函数呢?这时候就需要用到我们的包装器。

(1)function

function是一个适配器,其底层就是一个类模板。

其类原型如下:

#inclued template 
class function
{}
Ret:可调用对象的返回值类型
Args:这里是一个可变参数 可调用对象参数列表

function的应用:

int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator() (int a, int b){return a + b;}
};

我们完成对这些函数的包装器。

对类函数包装:

包装器对类成员的包装也常用。不过相对于可调用对象而言,它传入的参数会多一个。

class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
}
值得注意的是,若要访问类成员函数。
需要在function参数加 该类类型。 并且在使用时需要进行传参。

对于静态函数而言,可以不需要"&",但是对于成员函数必须要"&"。因此,最好的写法是,都取"地址"。

因此,有了function包装器,我们可以尝试着对那个我们之前面对的问题(解决模板的效率低下,实例化多份)做出一定的回应。

此时,当函数实例化时,函数内的count只有一个。也就是说只实例化出了一个函数。

包装器的其他应用场景

包装器还有其他应用的场景,并使用起来后,更加简洁、便于维护。下面是一个leetcode题。

进行更改后,可以变成这样。

(2)bind

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可
调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。

其原型如下:

template 
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template 
/* unspecified */ bind (Fn&& fn, Args&&... args);

这个函数模板,时常用来固定下来一些参数,也可以实现传参参数的顺序调整。

placeholders:

这是bind的参数,是一个命名空间。

bind函数应用:

如果我们需要调用一个普通函数,需要这样的用法。

如果我们需要调用一个成员函数,需要这样的用法。


总结

那么以上也就是本篇的所有内容,"C++11的语法让C++不再是C++",这句话现如今让我深有体会。当然,这些内容有一些是十分好用的,比如说lambda表示,function在一些场景时使用,也是十分"香的"。然而,深入学习一门语言本身就是这样得繁琐,乏味。也望诸君的努力不会白费。

本篇到此结束,感谢你的阅读。

祝你好运,向阳而生~

相关内容

热门资讯

公司消防安全管理制度 公司消防安全管理制度1、目的对消防安全进行控制管理,保障企业的健康、稳定发展。2、适用范围适用于本公...
锅炉工岗位职责 锅炉工岗位职责  岗位职责确定职责  1.根据工作任务的需要确立工作岗位名称及其数量;  2.根据岗...
保安公司保密制度 保安公司保密制度  在社会一步步向前发展的今天,制度的使用频率呈上升趋势,制度是指一定的规格或法令礼...
幼儿园餐具消毒卫生制度 幼儿园餐具消毒卫生制度幼儿园餐具消毒卫生制度1、幼儿餐后餐具立即清洗消毒,做到使用一次、清洗消毒一次...
VS2017+Qt5.12环境... VS2017+Qt5.12环境搭建在学习一门新技术时,最令人头疼的莫过于布置环...
亚马逊的这些“坑”,龙哥不允许... 随着口罩的放开,很多人都把目光投向了跨境电商这个领域。讲到跨境电商,肯定...
公司人员宿舍管理制度 公司人员宿舍管理制度(通用8篇)  在当今社会生活中,越来越多人会去使用制度,制度是指在特定社会范围...
防洪防汛通知 防洪防汛通知(精选15篇)  在当今社会生活中,用到通知的地方越来越多,通知根据根据适用范围的不同可...
人事部管理制度 人事部管理制度我自己认为自己在工作中是一个比较幸运的人,回顾自己工作的五年经历,让我感触很深。五年前...
实验工作计划 精选实验工作计划十篇  光阴迅速,一眨眼就过去了,很快就要开展新的工作了,不妨坐下来好好写写计划吧。...
接口测试——requests接... 1. requests库介绍与安装 requests库介绍 requests是一款非常火爆且常用的P...
Application使用内核... 在kernel模块里面创建链表_内核模块中简单的插入链表_蓝牙先生的博客-CSDN博客使用QEMU搭...
公函的涵义和用途 公函的涵义和用途  公函有什么涵义和用途呢?下面就由小编告诉大家吧,希望对大家有帮助。  函的涵义和...
济源职业技术学院招生章程 济源职业技术学院招生章程  济源职业技术学院招生章程  第一章总则  第一条 为保证济源职业技术学院...
幼儿园社区活动工作计划 幼儿园社区活动工作计划  时间是箭,去来迅疾,又迎来了一个全新的起点,现在就让我们好好地规划一下吧。...
仓库安全管理制度 仓库安全管理制度(通用5篇)  在不断进步的时代,很多地方都会使用到制度,制度是国家机关、社会团体、...
JavaScript数据结构与... 定义 动态规划(Dynamic Programming)是一种将一个问题...
机器学习必知的基础概念(Fun... 机器学习必知的基础概念(Fundamental Theories of Machine...
LeetCode 91. 解码... 题目: 链接:LeetCode 91. 解码方法 难度:中...
ABC294(A-F) AtCoder Beginner Contest 294 Contest Duration: 202...