10 函数、Lambda 表达式⚓︎
约 1024 个字 111 行代码 预计阅读时间 7 分钟
函数、Lambda 表达式⚓︎
引入
这是一个函数模板的例子,它能够计算任何类型的容器内单个指定元素的个数。
思考这个函数模板能够计算多个指定元素的个数?比如将 *iter == target
换成 isVowel(*iter)
(是否为元音字母)
template <typename InputIt, typename DataType>
int count_occurrences(InputIt begin, InputIt end, DataType target)
{
int count = 0;
for (auto iter = begin; iter != end; ++iter)
if (*iter == target) count++;
return count;
}
// ...
std::string str = "Tchaikovsky";
cout << "Occurrence of the letter k in Tchaikovsky: "
<< count_occurrence(str.begin(), str.end(), 'k') << endl;
谓词函数⚓︎
返回值类型为布尔(boolean)类型的函数称为谓词(predicate
// predicate 1
bool isVowel(char c)
{
std::string vowels = "aeiou";
return vowels.find(c) != std::string::npos;
}
// predicate 2
bool isMoreThan(int num, int limit)
{
return num > limit;
}
现在将前面那个函数模板的 target
参数改为一个含单参的谓词函数,修改后如下所示:
template <typename InputIt, typename UniPred>
int count_occurrences(InputIt begin, InputIt end, UniPred pred)
{
int count = 0;
for (auto iter = begin; iter != end; ++iter)
if (pred(*iter)) count++;
return count;
}
// ...
std::string str = "Tchaikovsky";
count_occurrence(str.begin(), str.end(), isVowel) ;
函数指针⚓︎
上面函数模板中的 UniPred
是一个函数指针(function pointer
上面的函数模板并不是一个很好的泛型编程,因为它那个作为参数的谓词函数是单参的,那么就必然有一定的局限性。如果尝试将单参换成双参:
template <typename InputIt, typename BinPred>
int count_occurrences(InputIt begin, InputIt end, BinPred pred)
{
int count = 0;
for (auto iter = begin; iter != end; ++iter)
if (pred(*iter, ???)) count++;
return count;
}
可以发现不能直接这么做,因为我们无法传递谓词函数的第二个参数——这时就需要用到 Lambda 表达式。
Lambda 表达式⚓︎
它是一种内联、匿名的函数,能够发现与它位于同一范围内的变量(即使变量在函数的外部
注:建议在函数主体相对较为简单的时候使用,主体过于复杂时还是用函数指针吧。
基本格式:
各种捕获从句:
[]
:不捕获东西[limit]
:捕获变量limit
的值[&limit]
:捕获变量limit
的引用[&limit, upper]
:捕获变量limit
的引用和变量upper
的值[&, limit]
:捕获除变量limit
之外的变量的引用[&]
:捕获所有变量的引用[=]
:捕获所有变量的值
举个 :
利用 Lambda 表达式,我们修改原来的函数模板,如下所示:
template <typename InputIt, typename UniPred>
int count_occurrences(InputIt begin, InputIt end, UniPred pred)
{
int count = 0;
for (auto iter = begin; iter != end; ++iter)
if (pred(*iter)) count++;
return count;
}
int limit = 5;
auto isMoreThan = [limit] (int n) { return n > limit; }
std::vector<int> nums = {3, 5, 6, 7, 9, 13};
count_occurrences(num.begin(), nums.end(), isMoreThan);
函数对象⚓︎
目前不是很理解,待补充
函数对象(functor)是一种提供运算符(operator()
)实现的类。它能够创建“自定义”函数的闭包(closure
举个 :
class functor
{
public:
int operator() (int arg) const
{
return num + arg;
}
private:
int num;
}
int num = 0;
auto lambda = [&num] (int arg) { num += arg; };
lambda(5);
STL 中专门提供了一种标准的函数对象:std::function<return_type(param_types)> func;
,之前提到的 Lambda 表达式、函数对象和函数指针均可被投射到这一标准函数,而后者相比前三者更大,成本也更加高。
虚拟函数⚓︎
在介绍继承的最后稍微讲到了虚拟函数(virtual functionsvirtual
即可。
例子
// 基类
class Animal
{
void speak()
{
std::cout << "I'm an animal!" << std::endl;
}
}
// 子类
class Dog : Animal
{
void speak()
{
std::cout << "I'm a dog!" << std::endl;
}
}
void func(Animal * animal)
{
animal->speak();
}
int main()
{
Animal * myAnimal = new Animal;
Dog * myDog = new Dog;
func(myAnimal);
func(myDog);
}
在这个例子中,func()
函数接收基类的指针,因此它不会使用子类的函数。同样的问题出现在指向子类对象的基类指针上。如果想要用子类的函数覆写基类的函数,只需在父类的函数 speak()
前加关键词 virtual
,即 virtual void speak()
即可。
算法⚓︎
编程世界中经常提到的一条原则:绝不重复造轮子!于是 C++ 的 STL 很贴心地为开发者们提供了许多轮子,这里我们简单介绍其中的一个轮子——算法库。使用前需在添加 #include <algorithm>
即可。这些算法都是泛型函数、模板函数,包含了一些高效查找、排序、复杂数据结构的运算、智能指针等等的功能,它们都是工作在迭代器之上的。
部分算法:
any_of(), all_of(), none_of()
:检查所有元素是否满足某一条件for_each()
:将一个函数用于容器内所有元素find(), search()
:找到特定元素 / 一系列元素copy()
:从一个容器内复制,删除、增加元素到另一个容器内
前面提到的函数模板 count_occurrences()
实际上就是 STL 的 count_if()
算法。
评论区