当前位置: 首页 > news >正文

【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]值捕捉了xy,引用捕捉了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 类构造函数的实参

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关文章:

  • 存储器分类
  • 【网络】HTTP报文首部字段
  • Qt的WindowFlags窗口怎么选?
  • LeetCode 2906 统计最大元素出现至少K次的子数组(滑动窗口)
  • oracle怎样通过固化较优执行计划来优化慢sql
  • ant design pro 项目发布遇到登录页访问404
  • 【免费下载】2012-2023年全国夜间灯光数据
  • 从遍历序列构造二叉树:前序+中序与中序+后序的递归解法详解
  • JavaScript面试问题
  • tbb parallel_for 使用
  • 颜色分类,不靠“调色盘”:双指针 VS 计数排序的正面PK
  • 【Linux】服务自启动设置的方式
  • LayerSkip: Enabling Early Exit Inference and Self-Speculative Decoding
  • Java之BigDecimal
  • Silvaco仿真中victory process的蒙特卡洛(Monte Carlo)离子注入
  • 深入理解 Linux 权限管理:从基础到进阶
  • 【GESP】C++三级练习 luogu-B2117 整理药名
  • (三十二)Android开发中AppCompatActivity和Activity之间的详细区别
  • 2025运维工程师面试题1(答案在后一张)
  • 企业的AI转型:生死时速的进化之路
  • “女乘客遭顺风车深夜丢高速服务区”续:滴滴永久封禁两名涉事司机账号
  • 2025上海车展 | 当智驾不再让人兴奋,汽车智能化暗战升级
  • 演员刘美含二手集市被曝售假,本人道歉
  • 华侨城A:一季度营收53.63亿元,净利润亏损14.19亿元
  • 国家卫健委:工作相关肌肉骨骼疾病、精神和行为障碍成职业健康新挑战
  • 《奇袭白虎团》原型人物之一赵顺合辞世,享年95岁