02 函数式编程
函数式编程
函数式编程的主要思想其实就是“回调函数”,即把一个函数A当成参数传给另一个函数B,这样B的就可以根据传入形参的不同,而能够做更多的事情
实现回调函数主要可以通过以下几种方式传入可调对象作为形参:
- 函数指针
- lambda表达式
std::function
- 函数对象(重载了
()
运算符的类)
函数指针
1.定义:函数指针是指向一个函数入口地址的指针。C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数
2.语法:
<返回值类型> (*<函数指针名>)(<形参列表>)
,如果形参列表为void,则说明没有参数。
(*<函数指针名>)是一个整体,括号不能省
1 | //如: |
使用时要注意:
- 参数的数量、类型保持一致
1 | void test04() |
3.函数指针数组:顾名思义,就是一个数组,数组的每一项都指向不同的函数
1 | void (*p[3])() = { test01,test04,test02 };//数组的每一项都指向不同的函数 |
4.typedef 在函数指针中的用法
在函数指针中,也可用typedef起别名,从而快速的声明函数指针,但用法和其他地方不太一样。
简单的理解方法:将函数指针看成是一个模板类,只有正确的写出”模板类型“(形参列表),才能定义出对象。
1 | typedef int (*MyFUN)(int,int);//MyFUN就成了int (*MyFUN)(int,int)的别名 |
使用MyFUN
这个别名代表了整个函数指针类型,就像typedef name vector<int>
一样
重要!!!!!!:
MyFUN 也可以做函数的形参,但传入被调用的返回值类型不是MyFUN,而由typedef那句话决定.
比如LVGL的event回调

这里形参中回调函数的类型是 lv_event_cb_t
实际是他是typedef void (*lv_event_cb_t)(lv_event_t * e);
即给函数指针起的一个别名,因此在传入回调函数时,得传入返回值是void、形参是lv_event_t*数据类型的函数
1 | static void btn_event_cb(lv_event_t* envent) |
实际该函数lv_obj_add_event_cb()的调用流程应该是:首先 lv_event_cb_t
一个函数指针event_cb
指向你传入的那个回调函数的地址,然后在程序内用这个函数指针进行函数调用操作。
5.通过结构体 + 函数指针 实现C语言中的面向对象(成员函数)
1 | typedef struct |
lambda表达式
Lambda 表达式(也称为匿名函数)是 C++11 引入的一种便捷的语法,它允许你在需要一个函数对象的地方快速定义一个函数,而不需要显式地定义一个完整的函数或函数对象类。Lambda 表达式通常用于简短的、临时的函数,它们可以捕获并包含其外部作用域中的变量。
Lambda 表达式的基本语法如下:
1 | [capture] (parameters) -> return_type { function_body } |
- capture:捕获子句,用于指定可以从 lambda 表达式外部捕获哪些变量。可以使用
[]
(不捕获任何变量)、[&]
(通过引用捕获所有外部变量)、[=]
(通过值捕获所有外部变量)、或者通过名称显式捕获变量。 - parameters:参数列表,定义了 lambda 表达式可以接受的参数。
- return_type:返回类型(非必须),如果你希望明确指定 lambda 表达式的返回类型,可以在这里指定。如果不指定,默认情况下,编译器会根据函数体推断返回类型。
- function_body:函数体,即 lambda 表达式要执行的代码。
一个简单的例子:
1 | int value = 5; |
std::function
std::function
是 C++11 标准库中的一个模板类,定义在<functional>
头文件中。它是一个通用的多态函数封装器,可以存储、调用和传递任何可调用对象,包括普通函数、成员函数、Lambda 表达式、函数对象以及成员函数指针。
基本用法:
1 | int add(int a, int b) { |
使用示例:
1 |
|
std::function
也可以作为函数的形参,以此实现回调函数
1 |
|
如果
std::function
作为函数的形参,同时我们想传入一个类的成员函数,也就是我们想让回调函数是一个类的成员函数,有2个实现方式:
1.使用lambda表达式
1 | void fun(std::function<void()> f) { |
2.使用std::bind
std::bind
是C++标准库中的一个函数模板,它用于创建一个新的可调用对象(函数、Lambda表达式、函数对象或成员函数),这个新的可调用对象在被调用时会以特定的参数序列调用原函数。
std::bind
的声明如下:
1 | template< class F, class... Args > |
这里的F
是可调用对象的类型,Args...
是参数列表
使用std::bind
的基本步骤:
- 选择要绑定的函数:这可以是普通函数、成员函数、Lambda表达式或函数对象。
- **调用
std::bind
**:传递函数和要绑定的参数。 - 获取结果:
std::bind
返回一个std::function<ReturnType(Args...)>
类型的对象,你可以像调用普通函数一样调用它。
下面给一些例子:
1 | int add(int a, int b) { |
1 | class MyClass { |
指针函数
1.定义:它本身是一个函数,但是返回值是一个指针。
2.语法:
<返回值类型>* <函数指针名>(<形参列表>)
,如果形参列表为void,则说明没有参数。
1 | int* fun(int a); |
1 | int* func(int n) |
使用时要注意,不能局部变量的地址,因为局部变量存在stack区,函数结束后就被销毁了,这样a就成了野指针。
回调函数
1.定义:回调函数就是一个被一个形参列表中带有函数指针的函数调用的函数。 回调函数并不是由实现方直接调用,而是在特定的事件或条件发生时由另外一方来调用的。目的类似于面向对象中的多态,为了减少代码量。但又有区别。
1 | int func(int a,int b,int (*p)(int,int)); |
1 | int callback(int a) |
- STM32 HAL库中的回调函数不是真正意义上的回调函数,因为他并没有函数指针,但是从使用场景看,确实是回调函数。