c++复习(set,lambada表达式)

set容器

set容器;(有个小知识点,set容器,元素只能出现一次,并且插入可以从小到大排序)
set是一个集合 和康托前辈的一样 集合中的元素不重复 且集合中的元素是有序的(自动有序化) TY菌介绍说其内部实质是一个平衡树

set不是数组 只能通过迭代器(iterator)把里面的元素倒出来 迭代器相当于是指针 扫描的是地址 因此输出的时候需要用*variation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <iomanip>
using namespace std;
bool cmp(int a, int b) {
return a > b;
}
int main() {
int a[] = {7, 3, 5, 9, 1};
sort(a, a + 5, cmp);
for (int i = 0; i < 5; i++)
cout <<setfill('*')<<setw(6)<< a[i] << ", ";
cout << endl;
return 0;
}

image

lambada表达式

闲话少说,归入正题,捕获的方式可以是引用也可以是复制,但是具体说来会有以下几种情况来捕获其所在作用域中的变量:

[]:默认不捕获任何变量;
[=]:默认以值捕获所有变量;

[&]:默认以引用捕获所有变量;

[x]:仅以值捕获x,其它变量不捕获;

[&x]:仅以引用捕获x,其它变量不捕获;

[=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;

[&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;

[this]:通过引用捕获当前对象(其实是复制指针);

[*this]:通过传值方式捕获当前对象;

在上面的捕获方式中,注意最好不要使用[=]和[&]默认捕获所有变量。首先说默认引用捕获所有变量,你有很大可能会出现悬挂引用(Dangling references),因为引用捕获不会延长引用的变量的声明周期:

std::function<int(int)> add_x(int x)
{
return & { return x + a; };
}

因为参数x仅是一个临时变量,函数调用后就被销毁,但是返回的lambda表达式却引用了该变量,但调用这个表达式时,引用的是一个垃圾值,所以会产生没有意义的结果。你可能会想,可以通过传值的方式来解决上面的问题:

std::function<int(int)> add_x(int x)
{
return = { return x + a; };
}

是的,使用默认传值方式可以避免悬挂引用问题。但是采用默认值捕获所有变量仍然有风险,看下面的例子:

class Filter
{
public:
Filter(int divisorVal):
divisor{divisorVal}
{}

std::function<bool(int)> getFilter() 
{
    return [=](int value) {return value % divisor == 0; };
}

private:
int divisor;
};

这个类中有一个成员方法,可以返回一个lambda表达式,这个表达式使用了类的数据成员divisor。而且采用默认值方式捕捉所有变量。你可能认为这个lambda表达式也捕捉了divisor的一份副本,但是实际上大错特错。问题出现在哪里呢?因为数据成员divisor对lambda表达式并不可见,你可以用下面的代码验证:

// 类的方法,下面无法编译,因为divisor并不在lambda捕捉的范围
std::function<bool(int)> getFilter()
{
return divisor {return value % divisor == 0; };
}

那么原来的代码为什么能够捕捉到呢?仔细想想,原来每个非静态方法都有一个this指针变量,利用this指针,你可以接近任何成员变量,所以lambda表达式实际上捕捉的是this指针的副本,所以原来的代码等价于:

std::function<bool(int)> getFilter()
{
return this {return value % this->divisor == 0; };
}

尽管还是以值方式捕获,但是捕获的是指针,其实相当于以引用的方式捕获了当前类对象,所以lambda表达式的闭包与一个类对象绑定在一起了,这也很危险,因为你仍然有可能在类对象析构后使用这个lambda表达式,那么类似“悬挂引用”的问题也会产生。所以,采用默认值捕捉所有变量仍然是不安全的,主要是由于指针变量的复制,实际上还是按引用传值。

lambda表达式还用于对象的排序准则:

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
class Person
{
public:
Person(const string& first, const string& last):
firstName{first}, lastName{last}
{}

Person() = default;

string first() const { return firstName; }
string last() const { return lastName; }
private:
string firstName;
string lastName;
};

int main()
{
vector<Person> vp;
// ... 添加Person信息

// 按照姓名排序
std::sort(vp.begin(), vp.end(), [](const Person& p1, const Person& p2)
{ return p1.last() < p2.last() || (p1.last() == p2.last() && p1.first() < p2.first()); });
// ...
return 0;
}

參考博客

链接:https://www.jianshu.com/p/d686ad9de817