系列文章目录
文章目录
前言
C++ 这门编程语言的历史可以追溯至 1979 年,当时的 Bjarne Stroustrup(C++ 之父,后续简称 Stroustrup)还在使用 Simula 语言进行开发工作。
1998 年,C++ 标准委员会发布了第一版 C++ 标准,并将其命名为 C++ 98 标准。据不知名人士透露,《带注释的C++参考手册》这本书对 C++ 98 标准的制定产生了很大的影响。
经过作者的不断迭代,一本书往往会先后发布很多个版本,其中每个新版本都是对前一个版本的修正和更新。C++ 编程语言的发展也是如此。截止到目前(2020 年),C++ 的发展历经了以下 3 个个标准:
2011 年,新的 C++ 11 标准诞生,用于取代 C++ 98 标准。
2014 年,C++ 14 标准发布,该标准库对 C++ 11 标准库做了更优的修改和更新;
2017 年底,C++ 17 标准正式颁布;
虽然学习 C++11 需要花些时间,但这是非常值得的;C++11 非常实用,它不但提高了开发效率,还让程序更加健壮和优雅。程序员应该乐于升级换代已有的知识,而学习和使用 C++11 早就是大势所趋。
|版本声明:山河君,未经博主允许,禁止转载
一、Lambda表达式
1.作用
Lambda表达式的定义其实不用多说,就是隐式的表达一个函数。但有人会觉得好端端的,干啥非得把函数名给隐式掉,显示它不香么?
Lambda使用场景多为在STL库中,有时必须显示创建一个函数接口用于调用,下面会举例子,又或者在某个类中,必须要创建一个接口用于功能(例如字符格式转换),但是放在类中和类功能就不太合适,专门放在一个功能类里又没有别的地方使用,这个时候就可以使用匿名函数。
2.表达式格式
一个完整的Lambda匿名函数表达式如下:
[capture list] (params list) mutable exception-> return type { function body }
选项 | 含义 |
---|---|
capture list | 捕获外部变量列表 |
params list | 形参列表 |
mutable | 用于是否可以修改捕获的变量 |
exception | 异常设定 |
return type | 返回类型 |
function body | 函数体 |
是不是看起来很复杂,以上除了capture list和function body是不可省略的,其它的都可以跟据使用需求进行省略,经常使用的格式有以下几种:
格式 | 使用 |
---|---|
[capture list] (params list) -> return type {function body} | 有参数、有返回值 |
[capture list] (params list) {function body} | 有参数、无返回值 |
[capture list] {function body} | 无参数、无返回值 |
是不是还是感觉很复杂,不必担心,我们直接看一下使用方式
二、匿名函数基础使用
1.最简单用法
其实最简单的格式为
[capture list] {function body}
如下:
[]{cout << 1;}
函数体只输出,不传参,并且没有返回值。
2.简单使用
STL中有个sort函数用于进行排序,一般默认进行升序,也可以指定排序方式,如下:
bool compare(int x, int y)
{
return x > y;
}
int main(int argv, char* argc[])
{
int nArray[] = { 1,34,63,234,546,123,35,23 };
sort(nArray, nArray + sizeof(nArray) / 4, compare);
return 0;
}
有没有感觉如果这样写,加如是在类中或者其他地方调用,函数作用不伦不类,也很繁琐,如果是用匿名函数,格式为
[capture list] (params list) -> return type {function body}
int main(int argv, char* argc[])
{
int nArray[] = { 1,34,63,234,546,123,35,23 };
sort(nArray, nArray + sizeof(nArray) / 4, [](int x, int y)->bool { return x > y; });
return 0;
}
3.获取匿名函数指针添加函数名
上述例子就可以看出,匿名函数虽然没有函数名,但是实际在sort使用时候,还是传入了函数指针,那么既然我们能获取到函数指针,我们就可以赋予匿名函数一个函数名。
auto print = [](int x)->bool {cout << x << endl; };
int main(int argv, char* argc[])
{
print(10);
return 0;
}
三、外部变量的使用
1.外部变量使用格式
外部变量格式 | 功能 |
---|---|
[] | 空方括号表示当前 lambda 匿名函数中不导入任何外部变量 |
[=] | 只有一个 = 等号,表示以值传递的方式导入所有外部变量 |
[&] | 只有一个 & 符号,表示以引用传递的方式导入所有外部变量 |
[val1,val2,…] | 表示以值传递的方式导入 val1、val2 等指定的外部变量,同时多个变量之间没有先后次序 |
[&val1,&val2,…] | 表示以引用传递的方式导入 val1、val2等指定的外部变量,多个变量之间没有前后次序 |
[val,&val2,…] | 以上 2 种方式还可以混合使用,变量之间没有前后次序 |
[=,&val1,…] | 表示除 val1 以引用传递的方式导入外,其它外部变量都以值传递的方式导入 |
[this] | 表示以值传递的方式导入当前的 this 指针 |
估计看到这里,有人会不明白什么是外部变量,我们为什么要结合外部变量一起使用?这是因为在实际使用时候,很多情况下我们无法传入我们想传入的参数,例如STL中遍历容器函数for_each:
int main(int argv, char* argc[])
{
vector<int> vecTest = { 1,2,3,4,5,6 };
for_each(vecTest.begin(), vecTest.end(), [](int i)->void {if (i < 5) cout << " " << i; });
return 0;
}
该段代码是遍历容器中小于5的值并且进行打印,但是这个时候如果不想打印小于5而是根据条件打印小于x的数值,这样写就不满足了,这个时候就需要直接使用外部变量,例如:
int main(int argv, char* argc[])
{
int x = 4;
vector<int> vecTest = { 1,2,3,4,5,6 };
for_each(vecTest.begin(), vecTest.end(), [x](int i)->void {if (i < x) cout << " " << i; });
return 0;
}
2.外部变量的使用
以下几种可以混合使用
(1)值捕获
int main(int argv, char* argc[])
{
int a = 4;
auto fun = [a]{cout << a << endl; };
return 0;
}
(2)引用捕获
int main(int argv, char* argc[])
{
int a = 4;
auto fun = [&a]{cout << a << endl; };
return 0;
}
(3)隐式捕获
除了值捕获和引用捕获,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。
int main(int argv, char* argc[])
{
int a = 4;
int j = 5;
auto fun1 = [=]{cout << a << " " << j << endl; };
auto fun2 = [&] {cout << a << " " << j << endl; };
return 0;
}
四、进阶用法
1.修改权限
上文说过mutable用于是否可以修改捕获的变量,为什么存在是否可以修改捕获变量这个问题呢,是因为捕获变量要么是值传递、要么是引用传递,引用传递当然可以进行修改变量,那么有的时候如果不使用引用实际上是同样可以进行修改的。
局部变量:
int a = 10;
auto fun1 = [a] {a++; }; //编译报错
全局变量:
int all_num = 10;
int main(int argv, char* argc[])
{
auto fun1 = [] {all_num++; }; //编译通过
}
引用变量:
int a = 10;
auto fun1 = [&a] {a++; }; //编译通过
借助mutable
int a = 10;
auto fun1 = [a] ()mutable { a++; }; //编译通过
2.异常抛出
我们知道基本任何函数都可以进行抛出异常,同样的匿名函数也可以,如下两种写法都可以:
//显示的说明可以抛出异常
auto except = []()throw(int) {
throw 1;
};
try {
except();
}
catch (int) {
cout << "捕获到异常";
}
//隐式的
auto except1 = []{
throw 1;
};
try {
except1();
}
catch (int) {
cout << "捕获到异常";
}
但是如果明确不抛出异常,那么如下代码会段错误
auto except1 = []()noexcept{
throw 100;
};
总结
仍在持续更新中~
如果对您有所帮助,请点个赞!