CPP

环境配置

g++常用命令选项

选项 解释
-ansi 只支持ANSI标准的C语法,禁止GNU C的某些特色,如asm或typeof关键词
-c 只编译并生成目标文件
-DMACRO 以字符串“1”定义MACRO宏
-DMACRO=DEFN 以“DEFN”定义宏
-E 只运行C预编译器
-g 生成调试信息
-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY
-LDIRECTORY 指定额外的函数库搜索路径DIRECTORY
-lLIBRARY 连接时搜索指定的函数库LIBRARY
-m486 针对486进行代码优化
-o FILE生成指定的输出文件,用在生成可执行文件时
-O0 不进行优化理
-O/-O1 优化生成代码
-O2 进一步优化
-O3 再进一步,包括inline函数
-shared 生成共享目标文件
-static 禁止使用共享链接
-UMACRO 取消对MACRO宏的定义
-w 不生产任何警告信息
-Wall 生成所有警告信息

基本语法

命名空间存在的意义

为了避免有些变量名发生冲突,使用命名空间的方式来区分,也即加前缀。如C++标准库中定义了vector容器,同时自定义了一个vector类,为了区分,使用标准库中的相关容器名时都应加上std::的前缀,同理,自定义的类中也可加个自定义的前缀。

但在不发生冲突情况下可以使用添加using namespace std代码使接下来使用相关的类名时不需要添加前缀。

注释

1.块注释符(/……/)不可嵌套使用

2.#if 0 …… #endif属于条件编译,0为参数。

可使用其来实现嵌套注释,格式如下:

1
2
3
#if 0
code
#endif

#if 0改为#if 1则,code中的代码可以被执行,


tips:测试时使用#if 1执行测试代码, 发布后用#if 0屏蔽测试代码。


更加一般的写法是:

1
2
3
4
5
#if condition
code1
#else
code2
#endif

数据类型


相较于我学的C的新的内容,宽字符型wchar_t

1
typedef short int wchar_t;

wchar_t实际空间和short int一样。

除此之外,还有size_tptrdiff_t

前者是一种整型类型,保存一个整数,记录以一个大小(size),全称为size type,一种用来记录大小的数据类型,常用的sizeof(xxx)得到的结果就是size_t类型,它可用来做加减乘除。

后者是pointer difference type,记录两个指针间的距离的数据类型。

两者通常是由typedef实现。

新的类型修饰符:

修饰符 描述
volatile 变量可被意外改变,禁止编译器优化,与const“相对”
mutable 类成员可在const对象中修改

注意:默认情况下,intshortlong都是有符号的

数据类型 描述 大小(字节) 范围/取值示例
bool 布尔类型,表示真或假 1 truefalse
char 字符类型,通常用于存储 ASCII 字符 1 -128 到 127 或 0 到 255
signed char 有符号字符类型 1 -128 到 127
unsigned char 无符号字符类型 1 0 到 255
wchar_t 宽字符类型,用于存储 Unicode 字符 2 或 4 取决于平台
char16_t 16 位 Unicode 字符类型(C++11 引入,无符号) 2 0 到 65,535
char32_t 32 位 Unicode 字符类型(C++11 引入,无符号) 4 0 到 4,294,967,295
short 短整型 2 -32,768 到 32,767
unsigned short 无符号短整型 2 0 到 65,535
int 整型 4 -2,147,483,648 到 2,147,483,647
unsigned int 无符号整型 4 0 到 4,294,967,295
long 长整型 4 或 8 取决于平台
unsigned long 无符号长整型 4 或 8 取决于平台
long long 长长整型(C++11 引入) 8 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long long 无符号长长整型(C++11 引入) 8 0 到 18,446,744,073,709,551,615
float 单精度浮点数 4 约 ±3.4e±38(6-7 位有效数字)
double 双精度浮点数 8 约 ±1.7e±308(15 位有效数字)
long double 扩展精度浮点数 8、12 或 16 取决于平台

C++新增类型

数据类型 描述 示例
auto 自动类型推断 auto x = 10;
decltype 获取表达式的类型 decltype(x) y = 20;
nullptr 空指针常量 int* ptr = nullptr;
std::initializer_list 初始化列表类型 std::initializer_list<int> list = {1, 2, 3};
std::tuple 元组类型,可以存储多个不同类型的值 std::tuple<int, float, char> t(1, 2.0, 'a');

派生数据类型

派生的有数组、指针、引用(变量别名,示例:int& ref = x;)、函数、结构体、类、联合体(union,多个成员共享同一块内存)、枚举(enum,用户定义的整数常量集合)

引用

引用不是新定义了一个变量,而是给已有变量取了个别名,编译器不会为引用变量开辟内存空间,共用同一块内存空间。


==语法==: 类型& 引用变量名(对象名) = 引用实体

例:

1
2
int a = 10;
int& b = a; //b为a的别名,即b相当于a,共用一块内存空间,因此当我去修改b时a的值也会相应发生改变,就像对a的地址存储内容进行修改一样

特性
  • 引用在定义时须初始化,是定义引用变量时,即int& ref;是不合法的
  • 一个变量可有多个引用
  • 一个引用可继续有引用,即对引用的引用实际上也是对于初始数据的引用
  • 引用一旦引用一个实体,不能再引用其他实体,即:无法对初始化后的引用变量进行值的修改
  • 可以对任何数据类型做引用(变量、指针……)
  • 可以进行权限的保持或缩小,但不可进行权限的放大。例如:不可以对常数据类型进行引用(放大):const int a = 2; int& b = a; 是错误的;但可以对常数据类型进行常引用(保持),或对数据类型进行常引用(缩小)。
应用

1.交换两数:

1
2
3
4
5
6
7
//通过函数参数部分利用引用的形式,将实际的参数传入相对应的参数上,实现交换
void swap(int& x, int& y)
{
int temp = x;
x = y;
y = temp;
}

2.单链表的头结点修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//链表的结构体定义
typedef struct SingleNode {
struct SingleNode * pNext;
int data;
}SLNode;

//不使用引用
void PushFront1(SLNode ** PList, int x)
{
SLNode * newNode = (SLNode *)malloc(sizeof(SLNode));
newNode->data = x;
newNode->pNext = *PList;
*PList = newNode;
}

//使用引用
void PushFront2(SLNode *& PList, int x)
{
SLNode * newNode = (SLNode *)malloc(sizeof(SLNode));
newNode->data = x;
newNode->pNext = PList;
PList = newNode;
}

3.优化编译器因函数返回时产生的不必要临时变量

编译器在函数运行结束返回值时,无论变量存储在【栈区】、【堆区】还是【静态区】,都会先将其存放在临时变量中,回到相应调用处时再将临时变量中的内容拷贝到接受变量中。但对于存放在【静态区】的变量是不会因为函数栈帧的销毁而消亡的,因此对于此类变量,上述方式显然多此一举,对于效率有影响

通过传引用返回可以实现权力反转,实现优化。

例:

1
2
3
4
5
6
7
8
//对于引用返回而言,编译器不会产生临时变量了,返回的只是n的别名,相当于将n返回回去
int& Count()
{
static int n = 0;
n++;
//……
return n;
}

对于像静态变量、全局变量、上一层栈帧、malloc的等这些出了作用域不会被编译器销毁的对象,可以使用传引用返回

4.可以通过常引用对常量取别名,例:const int& a = 20;

5.临时变量具有常性,

1
2
3
4
5
6
7
double d = 2.2;
int& e = d;//error

double d = 2.2;
const int& e = d;//right

//因为再进行int& e = d;或const int& e = d;进行的实际上是将d的整数部分取出赋值给一个大小为4字节的临时变量,然后再赋值给e,由于临时变量具有常性,就像被const修饰一样,不能被修改,根据权限的特性,不能放大权限,所以第一种方式编译会出错,而第二种虽然编译没出错,但e只是对临时变量的引用,即只是临时变量的别名

类型别名

为现有类型定义别名通过typedefusing(C++11引入,例:using MyInt = int;

标准库类型

数据类型 描述 示例
std::string 字符串类型 std::string s = "Hello";
std::vector 动态数组 std::vector<int> v = {1, 2, 3};
std::array 固定大小数组(C++11 引入) std::array<int, 3> a = {1, 2, 3};
std::pair 存储两个值的容器 std::pair<int, float> p(1, 2.0);
std::map 键值对容器 std::map<int, std::string> m;
std::set 唯一值集合 std::set<int> s = {1, 2, 3};

枚举类型

若一个变量有几种可能的值,可定义为枚举类型。即将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。

枚举类型一般形式:

1
2
3
4
5
6
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
……
标识符[=整型常数],
}枚举变量;

若枚举没有”整型常数”,则从第一个标识符开始。


例:

1
2
enum color {red, green, blue} c;
c = blue;

上述代码定义了一个颜色枚举,变量c类型为color。


默认情况下第一个名称值为0,第二个为1,以此类推。但若赋予了特殊的值,则相应的值会发生改变。


例:

1
enum color {red, green = 5, blue};

其中blue的值为6,而red的值仍为0。


类型转换

静态转换(Static Cast)

将一种数据类型的值强制转换为另一种,常用于比较类型相似的对象间的转换,转换时不进行任何运行时类型检查,可能会引发运行时错误。


例:

1
2
int i = 10;
float f = static_cast<float>(i);

动态转换(Dynamic Cast)

用于继承层次结构中进行向下转换的机制,将一个基类指针或引用转换为派生类指针或引用,运行时进行类型检查,若转换失败,对于指针类型返回nullptr;对于引用类型抛出std::bad_cast异常。


语法:

dynamic_cast<目标类型>(表达式)(目标类型是指针或引用类型,表达式为需转换的基类指针或引用)


常量转换(Const Cast)

将const类型的对象转换为非const类型的对象,不能改变对象的类型,只能用于转换掉const属性。


例:

1
2
const int i = 10;
int& r = const_cast<int&>(i); // 常量转换,将const int转换为int

重新解释转换(Reinterpret Cast)

将一个数据类型的值重新解释为另一个,常用于不同数据类型间的转换,不进行任何类型检查,可能导致未定义行为。

typedef和#define间的区别

执行时间不同

typedef在编译阶段有效,因此会进行类型检查。

#define发生在预处理阶段,即编译之前,只进行简单而机械的字符串替换,不进行任何检查。

功能差异

前者用来定义类型的别名;后者不只可为类型取别名,还可定义常量、变量和编译开关等。

作用域不同

后者无作用域限制,前者有自己的作用域。

对指针的操作

两者修饰指针形式时,作用不同。

1
2
3
4
5
6
7
8
9
typedef int * pint;
#define PINT int *

int val1 = 1, val2 = 2;

const pint p1 = &val1;
//此处的p1不可更改,p1指向的内容可更改,相当于int * const p1,也就是只能更改指向的内容,不能更改指针本身的值;即地址不可更改,但地址中的内容可以更改
const PINT p2 = &val2;
//此处p2可以更改,但指向内容不可更改,相当于const int * p2或 int const * p2

变量类型

注意

1.不带初始化的定义:若带有静态存储持续时间的变量会被隐式初始化为NULL(所有字节的值都是0),其他所有变量的初始值是未定义的。

2.使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的)。可以使用extern在任何地方声明一个变量。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 变量声明
extern int a, b;
extern int c;
extern float f;

int main ()
{
// 变量定义
int a, b;
int c;
float f;
// 实际初始化
a = 10;
b = 20;
c = a + b;
cout << c << endl ;
f = 70.0/3.0;
cout << f << endl ;
return 0;
}

3.定义包含了声明,但声明不包含定义,如

1
2
int a = 0; //定义并且声明了变量a
extern int a; //只是声明了有一个变量a存在,但具体a在哪定义,需要编译器编译时找

C++中的左右值

左值

指向内存位置的表达式称为左值表达式,其可出现在赋值号的左边或右边。

右值

存储在内存中某些地址的数值。是不可对其进行赋值的表达式,即右值表达式可出现在赋值号右边,但不能出现在左边。

变量是左值,所以可以在左边。数值型的字面值为右值,不能被赋值,不能出现在左边。

C++变量作用域

共6种,全局作用域、局部作用域、语句作用域、类作用域、命名空间作用域和文件作用域

全局变量、局部变量、静态全局变量和静态局部变量

作用域

全局变量有全局作用域,只需在一个源文件中定义,就可作用所有源文件,其他不含全局变量定义的源文件需用extern关键字再声明。

静态局部变量有局部作用域,只初始化一次,从第一次初始化后直到程序运行结束一直存在,与全局变量不同的是其只对定义自己的函数体可见

局部变量只有局部作用域,自动对象,运行期间不一直存在,只在函数执行期间存在,一次调用执行结束后,变量撤销,占用内存收回。

静态全局变量有全局作用域,不同于全局变量,若程序包含多个文件,则静态全局变量作用于定义它的文件里,不能作用到其他文件里,即被static关键字修饰过的变量有文件作用域,这样即使两个不同的源文件都定义了相同名字的静态全局变量,也是不同的变量。

分配内存空间

全局变量、静态局部变量和静态全局变量都在静态存储区分配,而局部变量在栈中分配。

将局部变量修饰为静态是改变了其存储方式即改变了生存期,不改变作用范围;将全局变量修饰为静态是改变了作用域,限制其使用范围,不改变存储位置。

Tips

1.若全局变量仅在单个文件中访问,可将其修饰为静态全局变量。

2.若全局变量仅由单个函数访问,可将其修改为改函数的静态局部变量。

3.函数中必须使用static变量:如某函数的返回值为指针类型,则必须是static的局部变量的地址作为返回值;

4.MyClass::class_var中的::是作用域解析运算符,用于访问类的静态成员,而std::cout中的::用于指定coutstd命名空间中的对象。

5.对于全局变量的引用以及重新赋值,直接使用全局变量名会出现:变量不明确的问题。在变量名前加上::即可像正常变量一样使用。

常量

整型常量带后缀U或L,U表示无符号整数,L表示长整数,可大写可小写。


浮点常量由整数部分、小数点、小数部分和指数部分组成,可使用小数形式或指数形式表示。

小数形式需包含整数部分、小数部分,或同时包含两者。3.14159

指数形式,须包含小数点、指数,或同时有,带符号指数由e或E引入。314159E-5L


字符常量括在单引号中,若常量以L(仅当大写时)开头 ,其表示一个宽字符常量(如:L’x’),则此时其必须存储在wchar_t类型的变量中,否则,其为窄字符常量(如’x’),其可存储在char类型的简单变量中。

转义序列 含义
\? ?字符
\a 警报铃声(声音不是从声卡上放出来,来自主板上的蜂鸣器)
\b 退格键
\ooo 一到三位八进制数
\xhh 一个或多个数字的十六进制数

可以使用\做分割一个很长字符串常量,使之分行,例如:

1
2
string greeting = "hello,\
world!!!";

一般使用**#define预处理器const**来定义常量。

两者区别:

类型和安全检查不同

宏定义为字符替换,没有数据类型的区别,且此替换无类型安全检查,可能产生边际效应等错误。

const常量有类型区别,需在编译阶段进行类型检查。

编译器处理不同

宏定义是“编译时”,在预处理阶段展开,不能对宏定义进行调试。

const常量是“运行时”,类似一个只读行数据。

存储方式不同

宏定义直接替换,不分配内存,存储于程序的代码段中。

const常量需要进行内存分配,存储于程序的数据段中。

定义域不同

宏定义对全局有效,const只对对应定义处的函数有效。

定于后能否取消

宏定义可通过#undef使之前的宏定义失效

const常量定义后将在定义域内永久有效。

是否能作为函数参数

宏定义不能,const常量可以。


注意:const常量定义时必须赋初值,除非其是用extern修饰的外部变量。


const char* , char const* , char* const的区别

将一个声明从右向左读

将*读成 pointer to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char * const cp;
// cp is a const pointer to char

const char * p;
// p is a pointer to const char

char const * p;
// p is a pointer to const char, the same as const char * p

char ** p1;
// pointer to pointer to char

char * const * p2;
// pointer to const pointer to char

const char * const * p3;
// pointer to const pointer to const char

char ** const p4;
// const pointer to pointer to char

类型限定符

提供变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字.

限定符 含义
const const 定义常量,表示该变量的值不能被修改。
volatile 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。其往往用于多线程的修饰。
restrict restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict
mutable mutable 用于修饰类的成员变量。被 mutable 修饰的成员变量可以被修改,即使它们所在的对象是 const 的。
static 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。
register 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。
explicit 可阻止不应允许的经过转换构造函数进行的隐式转换的发生,即声明为explicit的构造函数不能在隐式转换中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//volatile实例
volatile int num = 20; //定义变量num,其值可能会在未知的时间被改变

//mutable 实例
class Example {
public:
int get_value() const {
return value_; //const关键字表示该成员函数不会修改对象中的数据成员
}
void set_value(int value) const {
value_ = value; //mutable关键字允许在const成员函数中修改成员变量
}
private:
mutable int value_;
}

//static实例
void example_function() {
static int count = 0; //static关键字使变量count存储在程序生命周期内都存在
count++;
}

//register实例
void example_function(register int num) {
//register关键字将建议编译器将变量num存储在寄存器中
//来提高程序执行速度
//但实际上是否会存储在寄存器中是由编译器决定的
}

//explicit实例
class Test1
{
public:
Test1(int n)
{
num = n;
}//普通构造函数
private:
int num;
};
class Test2
{
public:
explicit Test2(int n)
{
num = n;
}//explicit(显式)构造函数
private:
int num;
};
int main()
{
Test1 t1 = 12;//隐式调用其他构造函数,成功
Test2 t2 = 12;//编译错误,不能隐式调用其构造函数
Test2 t2(12);//显式调用成功
return 0;
}

存储类

定义变量/函数的范围(可见性)和生命周期,放置在它们修饰的类型之前。

存储类 含义
auto 默认存储类说明符,可省略。修饰的变量有自动存储期,即生命周期仅限于定义它们的块,常在栈上分配。
register 用于建议编译器将变量存储在CPU寄存器中以提高访问速度。
static 定义有静态存储期的变量或函数,生命周期贯穿整个程序的运行期。函数内,static变量的值在函数调用间不变;文件内或全局作用域中,有内部链接,只能在定义它们的文件中访问。
extern 声明有外部链接的变量或函数,可在多个文件间共享。默认情况下,全局变量和函数有extern存储类。在一个文件中使用extern声明另一个文件中定义的全局变量或函数,可以实现跨文件共享
mutable 用于修饰类中的成员变量,允许在const成员函数中修改这些变量的值。通常用于缓存或计数器等需要在const上下文中修改的数据。
thread_local 定义有线程存储期的变量,每个线程都有自己的独立副本。线程局部变量的生命周期与线程的生命周期相同。

auto存储类

常用于两种情况:声明变量时根据初始化表达式自动推断变量的类型声明函数时函数返回值的占位符

static存储类

指示编译器在程序的生命周期内保持局部变量的存在,不需要在每次进入和离开作用域时进行创建和销毁。使用static变量可在函数调用间保持局部变量的值。

作用于全局变量时,会使其作用域限制在声明它的文件中。

作用在类数据成员上时,会使仅有一个该成员的副本被类的所有对象共享。

extern存储类

其提供一个全局变量的引用,使其对所有程序文件都可见。

extern是用来在另一个文件中声明一个全局变量或函数,常用于当有两个或多个文件共享相同的全局变量或函数时。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//mian.cpp
#include <iostream>
int count;
extern void write_extern();

int main() {
count = 5;
write_extern();
}

//support.cpp
#include <iostream>

extern int count;

void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}

通过$ g++ main.cpp support.cpp -o write命令编译得到write可执行文件。再通过$ ./write或者$ .\write运行得到相应的输出Count is 5.

mutable存储类

使被修饰的类的成员变量能在const成员函数中被修改。

常用于需要不改变对象外部状态的情况下进行状态管理的场景,如缓存、延迟计算等。

缓存:在const函数中计算并缓存结果,不影响对象的外部状态。

状态跟踪:如日志计数器,跟踪调用次数等信息,避免对类逻辑进行侵入式修改。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

class Example {
public:
Example() : value(0), cachedValue(0) {}

// 常量成员函数
int getValue() const {
return value; // 读取常量成员
}

// 修改 mutable 成员
void increment() {
++value;
cachedValue = value * 2; // 修改 mutable 成员
}

int getCachedValue() const {
return cachedValue; // 读取 mutable 成员
}

private:
int value; // 常规成员,不能在 const 函数中修改
mutable int cachedValue; // 可修改成员,可以在 const 函数中修改
};

int main() {
const Example ex;
// ex.increment(); // 错误:无法在 const 对象上调用非 const 函数
// ex.value = 10; // 错误:无法修改 const 对象的成员

std::cout << "Value: " << ex.getValue() << std::endl;
std::cout << "Cached Value: " << ex.getCachedValue() << std::endl; // 输出为 0

return 0;
}

thread_local存储类

用于多线程环境中管理线程特有的变量。

使用其修饰的变量在每个线程中都有独立的实例,因此每个线程对该变量的操作不会影响其他线程。

具有如下性质:

  • 独立性:每个线程有独有的变量副本,不同线程间读写操作互不影响。
  • thread_local变量在线程结束时自动销毁
  • thread_local变量可进行静态初始化或动态初始化,支持在声明时初始化。

适合用于需要存储线程状态、缓存或避免数竞争的场景,如线程池、请求上下文等。

函数

Lambda函数与表达式

Lambda表达式将函数看作对象,Lambda表达式可以像对象一样使用,如可以将其赋给变量和作为参数传递,还可像函数一样对其求值。

具体形式:

[capture](parameters) mutable ->return_type{body}

(parameters)在不需要参数传递时可以包括括号一起省略。

mutable,lambda函数一般是一个const函数,mutable可以取消其常量性,若使用mutable,则即使参数为空,参数列表也不能省略。

->return_type是用于追踪返回类型形式声明函数的返回类型,无返回值时可全部省略。

{body}除了可以使用参数外,还可使用所有捕获的变量。

例如:

[](int x, int y){return x < y ;}

若无返回值可以表示为:

[capture](parameters){body}

例如:

[]{ ++global_x; }


一个更复杂的例子:

[](int x, int y) -> int { int z = x + y; return z + x; }

该例中z被创建来存储中间结果,其值不会保留到下一次该表达式再次被调用时。

Lambda表达式中可访问当前作用域的变量,这是Lambda表达式的闭包行为,变量传递的传值和传引用的区别,通过[]指定:

1
2
3
4
5
6
[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

对于[=][&]的形式,Lambda可直接使用this指针,但对[]的形式,要使用this,必须显式传入:[this](){ this->someFunc();}();


使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//输出字符串的lambda表达式
auto myLambda = []{cout << "Hello, world!" << endl;};
myLambda();


//省略返回类型的lambda表达式
auto basicLambda = [](int x, int y) { return x + y; };
//auto basicLambda = [](int x, int y) -> int {return x + y; };
cout << basicLambda(2, 3) << endl;


int i = 10;
int j = 20;
auto func = [=]{ //[=]将外部所有变量拷贝一份值到lambda函数内
cout << i << endl;
};
func();
auto func1 = [&]{ //[&]将外部所有变量按引用传入lambda函数内
cout << &i << endl;
};
func1();


//捕获this指针
class test
{
public:
void hello() {
cout << "hello world!!!" << endl;
};
void lambda() {
auto fun = [this]{ //捕获this指针
this->hello();//此处this调用的是class test的对象
};
fun();
}
};


//若要修改lambda表达式捕获的变量,需要加上mutable进行修饰
int x = 10;
auto add_x = [x](int a) mutable { x *= 2; return a + x; };
cout << add_x(10)