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

【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://www.xdnf.cn/news/2938.html

相关文章:

  • SICAR 标准功能块 FB3352 (MODE)工作模式功能块
  • 是否想要一个桌面哆啦A梦的宠物
  • 特征工程四-2:使用GridSearchCV 进行超参数网格搜索(Hyperparameter Tuning)的用途
  • 基于开闭原则优化数据库查询语句拼接方法
  • KenticoCMS 文件上传导致xss漏洞复现(CVE-2025-2748)
  • RN 获取视频封面,获取视频第一帧
  • 【免费下载】1985-2023年全国土地利用数据
  • 《算法笔记》10.5小节——图算法专题->最小生成树 问题 B: Freckles
  • 当前HPLC载波无法满足全量数据分钟级采集需求的主要原因
  • STM32 SPI通信协议
  • 从整体上把握操作系统的作用,以及理解进程状态是什么
  • EtherCAT转Profinet网关,包装产线的“语言翻译器”
  • python:练习:2
  • 查看Mysql版本
  • c/c++之信号处理<signal.h>
  • 【vue3】黑马程序员前端Vue3小兔鲜电商项目【五】
  • 问题排查:calss extends 后页面加载不出来(忘记加super),打包后不报错;遇到问题可以适当出去走一下,让脑子休息一下
  • AimRT 从零到一:官方示例精讲 —— 五、Parameter示例.md
  • WPF(Windows Presentation Foundation)的内容模型
  • 可视化图解算法: 判断是不是二叉搜索树(验证二叉搜索树)
  • SEO优化指南与实战技巧
  • centos安装部署配置kafka
  • Vue常用的修饰符有哪些有什么应用场景(含deep seek讲解)
  • 通用事件库IO多路复用技术选型与设计
  • 常见位运算总结
  • 塑料材料工程师简历模板
  • C#进阶学习(十七)PriorityQueue<TElement, TPriority>优先级队列的介绍
  • 阿里云服务器 篇十二:加入 Project Honey Pot 和使用 http:BL
  • 万象生鲜配送系统代码2025年4月29日更新日志
  • Java练习3