【C++11】新的类功能、lambda
📝前言:
这篇文章我们来讲讲C++11——新的类功能、lambda、包装器
🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏,Linux
文章目录
- 一,新的类功能
- (1)默认构造和默认赋值
- default和delete
- final与override
- 二,lambda
- lambda基础语法
- 捕捉列表
- lambda 的原理
一,新的类功能
(1)默认构造和默认赋值
在学习类和对象的时候,我们已经了解过了类的6个默认成员函数。
而C++11引入右值引用以后,又多增加了:移动构造函数和移动赋值运算符重载函数这两个默认成员函数。两者类似。
默认移动构造:
- 生成条件:1,当没有显式实现移动构造;2,且没有实现析构函数、拷贝构造、拷贝赋值中任意一个。编译器就会生成默认移动构造
- 行为:对于内置类型:按字节浅拷贝。对于自定义类型:调用对应类型的成员的移动构造,如果没有实现移动构造,则调用它的拷贝构造
默认的移动赋值重载函数:
- 生成条件:1,没有显式的实现移动赋值重载函数;2,且没有实现析构函数、拷贝函数,拷贝赋值中的任意一个。编译器就会生成默认的移动赋值重载函数。
- 行为:对于内置类型:按字节浅拷贝。对于内置类型:调用成员的移动赋值重载,如果没有,就调用它的拷贝赋值重载
移动构造/赋值和拷贝构造/赋值的关系:
如果提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。即:移动和拷贝两者的默认生成前提都是另一个没有实现。
示例1:
没有移动构造和移动赋值,但是有拷贝构造和拷贝赋值的时候(不会生成默认的):
int main()
{tr::string s1 = "hello world";tr::string s3 = tr::string("tr");s1 = tr::string("fighting");return 0;
}
运行结果:
- 第一个:构造是
s1
的 - 第二个:本来应该是:构造 + 移动构造(但是没有移动构造,且没有默认生成),变成了构造 + 拷贝构造,然后被优化成一个构造
- 第三、四个:构造 + 移动赋值 变成 构造 + 拷贝赋值
示例2:
没有析构、拷贝构造、拷贝赋值时,使用生成的默认的:
int main()
{tr::string s1 = "hello world";tr::string s3 = tr::string("tr");s1 = move(s1);return 0;
}
运行结果:
这里打印不出来啊,因为调的是系统默认生成的。但是我们可以看变量信息:
因为tr::string
的成员函数都是内置类型,所以在移动赋值的时候都用了浅拷贝。
如果自己实现了移动构造和移动赋值:
可见确实和上面的默认的效果一样。(前面构造是:编译器把构造 + 移动构造优化了)
default和delete
default
:用于显式地要求编译器生成默认版本的特殊成员函数(即使自己已经实现了)delete
:禁用成员函数,或要求编译器自动生成特定的默认成员函数
(注意这个delete
可不是和new
对应那个)
示例:
string(const string& s) = delete; // 要求不自动生成拷贝构造
void reserve(size_t n) = delete; // 禁止使用reserve成员函数
string(string&& s) = default; // 要求编译器生成默认的移动构造
注意:delete
的函数,就代表禁用,函数不能再实现,不然是重定义,default
也是。
即:一个函数(不构成重载)不能既被delete / default
,又被实现
final与override
final
:
- 用于类:表示类是最终类,不能被继承
- 用于虚函数:表示函数是最终函数,不能被重写
示例:
class Base final { // 用于类写法// 类的成员
};class Base{virtual void func() final { // 用于函数写法// 函数实现}
};
override
:帮助用户检查虚函数是否真正重写(书写正确不会报错,书写错误会报错)
示例:
class Shape {
public:virtual void draw() {std::cout << "Drawing a generic shape." << std::endl;}
};// 重写正确(如:基类中确实有这个虚函数,函数名没问题...)
class Circle final : public Shape {
public:void draw() override {std::cout << "Drawing a circle." << std::endl;}
};
二,lambda
lambda基础语法
这里我只记录C++中lambda的语法,以及一些细节知识点。不对lambda的意义做过多讲解。
- lambda表达式的本质是一个⼀个匿名函数对象
- lambda语法层面而言没有类型,如果要接收可以用auto对象(当然也可以不接收)
lambda表达式基本语法:
[捕抓列表] (参数列表) -> 返回类型 {函数体}
- 当参数列表 / 返回类型为空时,这两部分可以省略,
()
和->
也可以省。返回类型不为空可以省略,编译器自动推导 - 函数体 / 捕捉列表就算为空:
[ ]
和{ }
也不能省
基本示例:
auto lambda1 = [](int x, int y)->int {return x + y; };
int main()
{cout << lambda1(1, 1) << endl; // 输出 2 return 0;
}
捕捉列表
下面详细讲讲捕捉列表
lambda表达式默认只能使用,lambda函数体和参数中的变量,如果想使用外层作用域的变量就需要捕捉
捕捉有三种方法:
- 显式捕捉:值捕捉和引用捕捉:
[x,y, &z]
值捕捉了x
和y
,引用捕捉了z
。【值捕捉的值不能修改,相当于const
修饰,引用捕捉的值可以修改,且会改外面的(因为是别名)】 - 隐式自动捕捉:
=
表示隐式值捕捉,&
表示隐式引用捕捉。我们在lambda中用了什么变量,编译器会自动帮我们去捕捉。但不能[=, &]
- 混合捕捉:即上面的两种方法可以混合,但是第一个参数必须为隐式捕捉,如:
[=, &x, &y]
其他细节:
- lambda 表达式如果在函数局部域中,它可以捕捉 lambda 位置之前定义的变量,但不能捕捉静态局部变量和全局变量(静态局部变量和全局变量也不需要捕捉,可以直接用)。这也意味着 lambda 表达式如果定义在全局位置,捕捉列表必须为空。
- 在参数列表后面加
mutable
可以取消其值捕捉参数的常性,这样我们就可以修改它,但是这时候的修改不会改到外部的实参。
使用示例:
int x = 0;
// 捕捉列表必须为空,因为全局变量不用捕捉就可以用,没有可被捕捉的变量
auto func1 = []() {x++; };int main()
{// 只能用当前lambda局部域和捕捉的对象和全局对象int a = 0, b = 1, c = 2, d = 3;auto func1 = [a, &b]{//a++; 值捕捉的变量不能修改(有常性b++; // 引用捕捉的变量可以修改int ret = a + b;return ret;};cout << func1() << endl;// 隐式值捕捉// 用了哪些变量就捕捉哪些变量auto func2 = [=]{int ret = a + b + c;return ret;};cout << func2() << endl;// 混合捕捉1auto func4 = [&, a, b]{c++;d++;return a + b + c + d;};cout << func4() << endl;cout << a << " " << b << " " << c << " " << d << endl;// 局部的静态和全局变量不能捕捉,也不需要捕捉static int m = 0;auto func5 = []{int ret = x + m;return ret;};cout << func5() << endl;// 传值捕捉本质是⼀种拷贝,并且被const修饰了// mutable相当于去掉const属性,可以修改了// 但是修改了不会影响外⾯被捕捉的值,因为是⼀种拷贝auto func6 = [=]()mutable{a++;b++;c++;d++;return a + b + c + d;};cout << func6() << endl;cout << a << " " << b << " " << c << " " << d << endl;return 0;
}
运行结果:
lambda 的原理
从编译后的汇编指令层看,lambda就是一个编译器生成的对应的仿函数的类。
- 仿函数的类名是编译按⼀定规则⽣成的,保证不同的 lambda ⽣成的类名不同
- lambda参数/返回类型/函数体就是仿函数operator()的参数/返回类型/函数体
- lambda 的捕捉列表本质是⽣成的仿函数类的成员变量,也就是说捕捉列表的变量都是 lambda 类构造函数的实参
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!