目录
一、CPP之lamba表达式
1、实践体会lamba表达式
(1)lambda表达式使用举例:
if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; }))
{
std::cout << "All numbers are even\n";
}
lamba表达式:[](int i){ return i % 2 == 0; }
(2)使用函数对象实现类似于lambda表达式的效果
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct IsEven//若使用class关键字,则需将重载函数作为public成员,class和struct
{ //关键字默认权限不一样
bool operator()(int i)
{
return i % 2 == 0;
}
};
int main(int argc, char **)
{
vector<int> v(10, 3);
//if (all_of(v.begin(), v.end(), [](int i) ->bool {return i % 2 == 0;}))//方式一:使用lamba表达式
//IsEven()不传参,这个()对应使用构造函数进行初始化的参数,i是内部传参,迭代器遍历时容器中的元素
if (all_of(v.begin(), v.end(), IsEven()))//方式二:使用函数对象
{
std::cout << "All numbers are even\n";
}
else
{
std::cout << " Not all numbers are even\n";
}
return 0;
}
2、lambda表达式总结
(1)lambda表达式 就是一个匿名函数,相当于一次使用的、直接原地展开调用的函数
(2)lambda表达式也叫闭包,就是在别的地方无法调用的封包
(3)lambda表达式其实就是一个函数对象,在内部创建了一个重载()操作符的类
3、lambda表达式格式
(1)完整格式5部分:[参数捕获] (操作符重载函数参数) mutable或exception声明 ->返回值类型 {函数体}
(2)最简单的lambda表达式:[](){},调用执行时为[](){}();
(3)带传参的lambda表达式:[](int i){//i在这里可以用}
(4)使用auto将lambda表达式定义为一个变量,再以变量方式调用
(5)使用->int这种方式让lambda表达式函数返回相应类型的值
示例如下:
(2) [](){}();
(3) [](){cout << "hello" << endl;}();
[](int i){cout << "hello" << i << endl;}(5);
(4)、(5) auto func = [](int i) ->bool {cout << "hello" << i << endl; return false;};
bool x = func(4);
cout << "x = " << x << endl;
二、lambda表达式之参数捕获
1、什么是参数捕获
(1)实验:在lambda表达式外面定义int a,在表达式内部试图访问
(2)解决:在[]中增加捕获说明即可
#include <iostream>
using namespace std;
class test
{
private:
int sum;
public:
test(){};
test(int t){this->sum = t;};
void func(void)
{
cout << "sum = " << sum <<endl;
[&](){sum++;cout<<"lamba4: sum = " << sum <<endl;}();
[this](){this->sum++;cout<<"lamba5: this->sum = " << this->sum << endl;}();
}
};
int main(int argc, char *argv[])
{
int a = 5;
string b = "test";
[a](){cout << "lamba1: a = " << a << endl;}();
[&a](){a++; cout << "lamba2: a = " << a << endl;}();
auto func = [=](int i){cout << "lamba3: i = " << i << " a = " << a << " b = " << b << endl;};
func(4);
test s(10);
s.func();
return 0;
}
(3)总结:所谓参数捕获,就是让lambda表达式内部可以捕获并使用外部的变量
2、lambda表达式的捕获列表
(1)[] 空,完全不捕获
(2)[=] 等号,以值传参方式捕获(复制了一份传进来,这样表达式内修改了变量,但表达式外值
仍未变,与引用传参的效果不同),捕获范围是表达式所在作用范围(包括所在类的this)
(3)[&] &号,以引用传参方式捕获,捕获范围是表达式所在作用范围(包括所在类的this)
(4)[this] 只捕获lambda表达式所在类的this可访问的那些
(5)[a] 仅以值方式捕获a,其他全部不捕获
(6)[&a] 仅以引用方式捕获a,其他全部不捕获
(7)[a, &b] 仅以值方式捕获a,以引用方式捕获b,其余完全不捕获
(8)[=, &a, &b] 仅以引用方式捕获a和b,其余以值方式捕获
(9)[&, a, b] 仅以值方式捕获a和b,其余以引用方式捕获
3、lambda表达式总结
(1)lambda表达式提供一种单次使用的函数简写方式
(2)通过捕获列表,在lambda表达式内部也可以访问外部的变量,相当于函数传参
三、CPP函数适配器
1、什么是函数适配器
(1)适配器,adapter,用来在不适配的2端间对接的连接器
(2)函数适配器是在不同传参个数的函数间进行适配的技术
示例理解:
func1(int a)
func2(int a, char b)//可通过手动填充一个参数b进行适配
func3(int a){func2(a, 'A');}//func3则是一个函数适配器,适配了func1与func2
(3)几个概念:1元函数、2元函数、1元谓词、2元谓词
1元:具有一个参数
2元:具有两个参数
谓词:返回值是bool类型
2、C++的函数适配器
(1)早期C++98时,常用bind1st bind2nd
bind1st:预先填充第一个,释放第二个,bind2nd与其刚好相反
(2)C++11开始,引入加强版:std::bind
if (std::none_of(v.cbegin(), v.cend(), std::bind(std::modulus<int>(), std::placeholders::_1, 2)))
//std::placeholders::_1:表示释放谁,2:表示要填充的值,即将二元谓词的第一个元释放出来,第二个元预先填充,形成一个新的一元谓词
{
std::cout << "None of them are odd\n";
}
template< class InputIt, class UnaryPredicate >
bool all_of( InputIt first, InputIt last, UnaryPredicate p );//UnaryPredicate表示一元谓词
std::modulus::operator()//二元谓词
T operator()( const T& lhs, const T& rhs ) const;
(C++14 前)
constexpr T operator()( const T& lhs, const T& rhs ) const;
(C++14 起)
返回 lhs 除以 rhs 的余数。
一元谓词和二元谓词并不适配,故需要函数适配器bind
3、bind的学习
参考学习:https://blog.csdn.net/u013654125/article/details/100140328
#include <iostream>
#include <functional>
using namespace std;
int TestFunc(int a, char c, float f)
{
cout << a << endl;
cout << c << endl;
cout << f << endl;
return a;
}
int main()
{
auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 100.1);
bindFunc1(10);
cout << "=================================\n";
auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 100.1);
bindFunc2('B', 10);//char c ,int a,即B对应c,10对应a
cout << "=================================\n";
auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
bindFunc3(100.1, 30, 'C');
return 0;
}
四、STL典型泛型算法解读
1、for_each、transform、范围 for 循环(C++11)
参考学习:
https://zh.cppreference.com/w/cpp/algorithm/for_each
https://zh.cppreference.com/w/cpp/algorithm/transform
https://zh.cppreference.com/w/cpp/language/range-for
struct Sum
{
void operator()(int n) { sum += n; }
int sum{0};//使用{}赋初值,若struct Sum类型变量被定义,sum初值为0
};
for_each示例:
可能源码:
template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
for (; first != last; ++first) {
f(*first);
}
return f; // C++11 起隐式移动
}
使用示例:
std::vector<int> nums{3, 4, 2, 8, 15, 267};
auto print = [](const int& n) { std::cout << " " << n; };
std::cout << "before:";
std::for_each(nums.cbegin(), nums.cend(), print);//实现遍历元素并打印
std::cout << '\n';
transform示例:
可能源码:
版本一
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op)
{
while (first1 != last1) {
*d_first++ = unary_op(*first1++);
}
return d_first;
}
使用示例:
std::string s("hello");
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) -> unsigned char { return std::toupper(c); });
范围 for 循环示例:
可能源码:
属性(可选) for ( 初始化语句(可选)范围变量声明 : 范围表达式 ) 循环语句
使用示例:
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (const int& i : v) // 以 const 引用访问
std::cout << i << ' ';
std::cout << '\n';
for (auto i : v) // 以值访问,i 的类型是 int
std::cout << i << ' ';
std::cout << '\n';
for (auto&& i : v) // 以转发引用访问,i 的类型是 int&
std::cout << i << ' ';
std::cout << '\n';
const auto& cv = v;
for (auto&& i : cv) // 以转发引用访问,i 的类型是 const int&
std::cout << i << ' ';
std::cout << '\n';
for (int n : {0, 1, 2, 3, 4, 5}) // 初始化器可以是花括号初始化器列表
std::cout << n << ' ';
std::cout << '\n';
int a[] = {0, 1, 2, 3, 4, 5};
for (int n : a) // 初始化器可以是数组
std::cout << n << ' ';
std::cout << '\n';
for ([[maybe_unused]] int n : a)
std::cout << 1 << ' '; // 不必使用循环变量
std::cout << '\n';
for (auto n = v.size(); auto i : v) // 初始化语句(C++20)
std::cout << --n + i << ' ';
std::cout << '\n';
for (typedef decltype(v)::value_type elem_t; elem_t i : v)
// typedef 声明作为初始化语句(C++20)
std::cout << i << ' ';
std::cout << '\n';
for (using elem_t = decltype(v)::value_type; elem_t i : v)
// 别名声明作为初始化语句,同上(C++23)
std::cout << i << ' ';
std::cout << '\n';
}
2、再看几个泛型算法
https://zh.cppreference.com/w/cpp/algorithm
其他算法的学习方法与上边相同,打开上边的网址自主学习,授之以🐟不如授之以渔。
2、泛型算法总结
(1)理解 谓词 和 函数对象,是学习和使用泛型算法的基础
(2)泛型算法很多,但是套路是类似的,学会的关键是理解并会用这种套路
(3)不建议试图死记硬背所有泛型算法,建议实战中去熟悉、去用起来并记住
(4)能用现成泛型算法写代码就不要自己造轮子
(5)一个潜在问题警告:C++包的越来越多(层层封装),不要想当然,未经验证确认的代码都要有怀疑精神
注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。