C++方向知识汇总(四)
关于Lambda表达式
1.什么是lambda表达式?它解决了什么问题?
答:
lambda是c++11引入的新特性、本质就是匿名函数、底层就是仿函数
解决了可以在函数内部定义函数的情况,简化了代码,还可以作为算法函数的参数
可以捕获外部变量,比函数指针更灵活
避免了函数命名冲突
2.lambda表达式的捕获作用域?
答:
定义在一个函数内的lambda可以捕捉此函数内的变量,即使在此函数的子作用域定义的,如for,if等
3.lambda表达式的基本语法结构是什么?各部分含义是什么?
答:
【捕获列表】(函数参数)mutable可选(允许修改值捕获的值) noexcept可选(声明lambda不会抛异常) ->返回类型 {}函数体
4.捕捉列表有几种形式?分别有什么作用?
答:
[ ]空捕捉
[ var ] 值捕捉 捕捉var的副本(修改副本不影响外部,一般值捕获不能修改,默认const)
[ &var ] 引用捕获 修改影响外部
[ = ] 值捕获所有能捕获到的变量
[ & ] 引用捕获所有能捕获到的变量
[ = , &var ] 混合捕获,默认值捕获,个例引用捕获
[ & , var ] 混合捕获,默认引用捕获,个例值捕获
[ this ] 捕获当前指针对象(如果在类成员函数中,可通过this指针访问成员变量\函数)
[ *this ] C++17:捕获当前对象的副本(避免悬空引用)
5.lambda的mutable关键字有什么作用?举例说明?
答:
默认情况,值捕获的变量在lambda体内是const,值捕获的值不可以修改,虽然是副本,如果需要修改需要加上mutable,来取消其const属性
注意⚠️:mutable只能修饰lambda表达式和类非静态成员函数
6.lambda的返回值类型如何确定?何时必须显实指定返回类型?
答:
如果函数体内只有一条return,编译器可自动推导类型,无需显式指定
如果函数体内有多条return,或者无return,必须显式指定,无return 指定 void
7.lambda与函数指针、函数对象的区别是什么?
答:
lambda
编译器生成的匿名闭包类型、通过捕获变量、可作为参数传递(需要function包装)、适用于算法参数等
函数指针
函数类型的指针、不支持捕获、不需要包装直接传递、调用开销
函数对象
自定义类型、通过成员变量可以捕获、可直接传递、性能快、适合复杂逻辑
8.如何将lambda作为函数参数传递给函数?
答:
1.模板参数
2.function包装
9.lambda的底层原理是什么?闭包的本质是什么?
答:
编译器会将lambda表示式转换为一个匿名类(闭包类型),并在类中生成operator() 函数调用重载
捕获的变量会变为该类的成员变量
匿名类其实就是匿名的仿函数类,称为闭包类型,在需要时创建该类对象实例,称为闭包对象
实际编译器底层会转换为:
底层简单总结就是:
- 转为匿名仿函数类(闭包类型)
- 捕获的变量变为类的成员变量 构造时自动传参初始化 把捕获的变量值传参进去构造
- 调用类的()重载实现
- 实际就是对仿函数operator()的调用
闭包的本质是该匿名类的实例对象、其类型是唯一的(每个lambad对应不同的类型)
闭包的特性:闭包类型默认不可复制构造、无默认构造、赋值运算符
大小由捕获变量决定、空捕获大小1字节
闭包其实就是 具有函数性可以被调用 具有状态性能够保存变量 具有作用域扩越性 即使外部作用域销毁,闭包对象在,就能访问捕获的状态
10.lambda捕捉变量时可能存在哪些陷阱?如何避免?
答:
引用捕获变量,但是引用对象已经销毁了,导致悬空引用,会访问到已销毁的空间
解决方案:改为值引用,值引用 匿名类里有自己的变量副本
隐式捕获的滥用,导致生命周期问题
解决方案:显式指定捕获变量
类成员函数调用this,但是对象已经销毁,导致调用lambda表达式崩溃
11.C++14、17对lambda有哪些扩展?
答:
如 14支持参数用auto声明 17 可以*this捕获对象副本,而不是对象本身,避免this对象销毁问题
12.lambda在多线程场景有哪些注意事项?
答:
如果线程生命周期大于捕获变量、禁止引用捕获栈上的变量(防止悬空引用)
值捕获的变量是副本、线程内修改不影响外部,需同步时用互斥锁或者原子变量
捕获this指针时,确保this对象不被销毁
避免捕获临时对象的引用,如返回的临时变量
13.用lambda实现一个简单的排序功能,对vector<pair<int,string>>按第一个元素升序、第二个元素降序排序
答:
14.如何用lambda封装一个延迟执行的函数(如资源释放)?
答: