《C++探幽:仿函数与lambda表达式》
《C++探幽:仿函数与lambda表达式》
仿函数
- 定义
- 仿函数(Functor)是一种重载了函数调用运算符(operator())的类的对象。它看起来像函数,但又具有对象的特性。本质上,仿函数是一个类,通过重载operator()来实现类似函数的行为。
- 特点
- 状态保存:仿函数可以有自己的成员变量,这意味着它可以保存状态。例如,在一个累加的仿函数中,可以保存当前的累加值。而普通的函数无法保存状态,每次调用都是独立的。
- 类型安全:仿函数的参数类型和返回类型在类定义时就已经确定,这使得它在类型检查方面更加严格。编译器可以在编译时检查参数类型是否匹配,减少运行时错误。
- 可定制性高:通过继承和修改仿函数类,可以很容易地扩展其功能。比如,可以继承一个基本的比较仿函数类,然后添加新的比较逻辑。
- 使用场景
- 算法库:在C++标准库的算法中,仿函数经常被用作参数。例如,在
std::sort函数中,可以传入一个自定义的比较仿函数来指定排序规则。如果要对一个整数数组按照绝对值的大小进行排序,就可以定义一个仿函数来实现这种特殊的比较逻辑。 - 事件处理:在一些事件驱动的程序中,可以使用仿函数来处理事件。例如,在图形用户界面(GUI)程序中,当用户点击按钮时,可以将一个仿函数绑定到按钮的点击事件上,这个仿函数可以包含处理按钮点击的逻辑,同时还可以保存一些与按钮相关的信息。
- 算法库:在C++标准库的算法中,仿函数经常被用作参数。例如,在
二、 lambda表达式
-
定义
Lambda表达式是一种匿名函数对象。它允许在代码中直接定义一个简单的函数,而不需要像定义普通函数那样写一个完整的函数声明和定义。Lambda表达式是从C++11标准开始引入的。
-
语法结构
一个典型的lambda表达式语法如下:
- body(函数体):包含要执行的代码。
- return_type(返回类型):在C++11中,返回类型是可选的,如果省略,编译器会根据函数体中的返回语句自动推导。从C++14开始,可以使用
auto来让编译器自动推导返回类型。 - parameters(参数列表):和普通函数的参数列表类似,可以为空,也可以有多个参数。
- capture(捕获列表):用于捕获外部变量。捕获方式有值捕获(用
[]表示,如[x]表示捕获变量x的值)和引用捕获(用[&]表示,如[&x]表示捕获变量x的引用)。还可以混合捕获,如[this, &x]表示捕获当前类的this指针和变量x的引用。 [capture](parameters)->return_type{body}
-
特点
- 简洁性:对于一些简单的函数逻辑,使用lambda表达式可以减少代码量。例如,在调用
std::sort时,如果只是简单的比较逻辑,使用lambda表达式可以直接在调用处定义比较规则,而不需要单独定义一个仿函数类。 - 局部性:lambda表达式定义在需要使用它的地方,这使得代码更加紧凑,逻辑更加清晰。它只在定义它的作用域内有效,不会像全局函数那样可能引起命名冲突。
- 捕获变量:可以方便地捕获外部变量,这使得lambda表达式在处理一些需要使用外部变量的场景时非常方便。例如,在遍历一个容器并对每个元素进行操作时,可能需要使用外部的一些变量来控制操作的逻辑。
- 简洁性:对于一些简单的函数逻辑,使用lambda表达式可以减少代码量。例如,在调用
-
使用场景
- 标准库算法:和仿函数一样,lambda表达式也可以作为标准库算法的参数。例如,在
std::for_each中,可以使用lambda表达式来对容器中的每个元素执行操作。如std::for_each(vec.begin(), vec.end(), [](int &x){x *= 2;});,这个lambda表达式将容器vec中的每个整数元素乘以2。 - 线程创建:在C++多线程编程中,可以使用lambda表达式来创建线程。例如,
std::thread t([](){std::cout << "Hello from thread" << std::endl;});,这个lambda表达式定义了线程要执行的函数体,使得线程的创建和任务的定义更加简洁。
- 标准库算法:和仿函数一样,lambda表达式也可以作为标准库算法的参数。例如,在
