C++ STL 使用教程
1.Lambda表达式
1.基础知识:函数的三件马甲:函数指针、函数对象与Lambda表达式。
一、函数指针:顾名思义,就是指向函数的指针。
有一个函数为 int add(int a,int b);则它的函数指针形式为:int (*p)(int a,int b)
1.定义函数指针
// 定义一种新的函数指针的数据类型
// 这种类型的函数指针可以指向的函数的返回值类型是void,
// 同时拥有一个int类型的参数
typedef void (* PRINTFUNC )(int);
// 使用新的数据类型定义多个同类型的函数指针
PRINTFUNC pFuncFailed;
PRINTFUNC pFuncPass;
这里,就定义了一种新的函数指针类型PRINTFUNC,它表示这种类型的函数指针可以指向一个返回值类型为void同时拥有一个int类型参数的函数。
完成函数指针的定义后,就可以用函数名给函数指针赋值,让它指向这个函数。
// 用函数名给函数指针赋值
pPrintFunc = PrintPass;
为了简单使用函数指针:可以使用auto进行。 auto pPrintFunc=PrintPass;
2. 使用函数指针:
(*
pPrintFunc
)(5);
3.为什么使用函数指针?
把函数指针用作参数,将函数传递给另一个函数,这个函数就被称之为回调函数。
回调函数的意义:
利用函数指针参数传递函数,然后回调可以改变一个函数的行为、对其行为进行参数化的自定义。正是因为这种灵活性,回调函数被广泛地应用在C++程序中,比如线程函数、通用算法等。当我们在实现某个算法时,我们往往只是实现算法的基本框架,而算法的具体规则,则交给回调函数去实现,也就是在算法中留下插口,留待算法的具体使用者去实现。这样,同一个算法可以适应不同的条件,从而达到通用的目的。
可以类比 sort中的一个函数。
4.用途:
STL中通用算法中,大量使用了函数指针形式的回调函数形式。
二、函数对象:
所谓函数对象,在语法上跟我们通常所见的类的对象没有什么本质的差别,唯一的特殊之处就是这个类定义了函数调用操作符(function-call operator),即operator()。而在函数调用操作符中,可以对数据进行处理,实现函数的所有功能。同时,因为类具有成员变量,可以将每次函数执行的状态数据保存到它的成员变量中,而在下次执行的时候可以访问这个成员变量从而得到上一次执行时的状态数据。这样,函数对象既可以像函数指针一样执行数据处理过程,但又不会像它那样健忘了,从而可以应用在更广泛的场景中。
1.函数对象定义:和其他对象类似
class FuncObjType { public: void operator() () { cout<<"Hello C++!"<<endl; } };
2.使用:
FuncObjType print; print();
3.意义:
既然函数对象是一个具体的实体对象,那么它既可以单独使用,也可以像函数指针一样被当成参数传递给其他函数,并在其他函数中使用。另外,函数对象在带来便利的同时并不会影响程序的性能。它一般没有构造函数和析构函数,因此,在创建或销毁函数对象的过程中不会有额外的性能消耗。同时,“( )”操作符往往是一个内联函数,编译器能内联它的代码,从而避免了与函数调用相关的性能损失。
4.和函数指针的区别:
1.函数对象能记住状态,而函数指针不能记忆数据。
5.用途:
STL预定义了许多常用的函数对象类,主要包括:1.算术操作 (plus,minus) 2.比较操作(equal_to,less)3.逻辑操作(logical_and)
三、
lambda表达式
匿名函数:lambda
1.定义
[变量使用说明符号](参数列表)->返回值数据类型 { //函数体 }
2.说明
中括号“[ ]”表示Lambda表达式的开始,用来告诉编译器接下来的代码就是Lambda表达式。在中括号中,可以指定Lambda表达式对当前作用域(也就是Lambda所在的大括号“{}”范围)中的变量的捕捉方式,所以这个中括号也可以称为“捕捉列表(capture list)”。如果我们希望在Lambda表达式内部以传值(复制)的方式使用当前作用域中的所有变量,则使用“[=]”表示,这就意味着Lambda表达式内访问到的变量只是外部同名变量的一个副本,在Lambda表达式内部对变量的修改不会影响到外部的同名变量。换句话说,也就是在Lambda内部只能读取外部变量的值但却没法对其进行修改。如果试图修改,将导致一个编译错误。如果中括号留空,默认情况下也表示以传值方式使用Lambda表达式外部的变量。
如果想在Lambda表达式内部对外部变量进行修改,则可以使用“[&]”代替“[=]”作为Lambad表达式的开始,这表示Lambda表达式将以传引用的方式捕捉当前作用域内的变量。这就意味着Lambda表达式内部的变量都是外部同名变量的引用,所以在Lambda表达式中对这些变量的修改将直接影响到当前作用域中变量本身。
需要与Lambda表达式传递多个数据,而同时各个数据的传递方式又各不相同,那么可以在中括号中的第一个位置用“&”作为Lambda表达式的默认传递方式,而那些需要以传值方式进行传递的变量,则可以单独在中括号中列出。
[&,a,b](参数列表)->返回值类型 { //函数体 }
3. 自定义使用 lambda的函数。
需要借助于function类模板。使用头文件#include<functional>
#include <functional> // 引入function类模板所在的头文件 // 可以接受Lambda表达式的mycount_if()算法 int mycount_if(const vector<int>& v, // 需要统计的容器 // 将函数指针类型更换为function<bool(int)>类型, // 表示它可以接受一个返回值为bool类型,同时拥有一个int类型参数 // 的函数指针或函数对象,自然也可以是相应类型的Lambda表达式 function<bool(int)> is) { // 函数体无需进行任何修改… } // … // 在mycount_if()算法中应用Lambda表达式 int nPass = mycount_if(vecScore, // 一个返回值为bool类型,同时拥有一个int类型参数的Lambda表达式 [=](int x) -> bool { return x >= 60; // 判断分数是否及格 });