【C++】C++11新特性——类的改进|lambda表达式
文章目录
- 一、类的改进
- 1.1 默认生成
- 1.2 移动构造函数
- 1.3 移动赋值重载函数
- 1.4 成员变量缺省值
- 1.5 强制生成默认函数的关键字default
- 1.6 禁止生成默认函数的关键字delete
- 1.6.1 C++98防拷贝
- 1.6.1 C++11防拷贝
- 二、lambda表达式
- 2.1 对比
- 2.2 lambda表达式语法
- 2.3 捕捉列表
- 2.4 函数对象与lambda表达式
一、类的改进
1.1 默认生成
C++ 的类有四类特殊成员函数, 它们分别是: 默认构造函数、 析构函数、 拷贝构造函数以及拷贝赋值运算符。
C++11 新增了两个:移动构造函数和移动赋值运算符重载
这两个成员函数在上一面介绍过:【C++】C++11新特性——右值引用
这些类的特殊成员函数负责创建、 初始化、 销毁, 或者拷贝类的对象。如果没有显式地为一个类定义某个特殊成员函数, 而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。
但是, 如果程序员为类显式的自定义了非默认构造函数, 编译器将不再会为它隐式地生成默认无参构造函数。
class A
{
public:A(const A& aa): _a(aa._a){}
private:int _a;
};int main()
{A a;// 不存在默认的构造函数return 0;
}
而对于移动构造函数和移动赋值运算符重载函数:
1.2 移动构造函数
如果没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器就会自动生成一个默认的移动构造函数。
这个默认生成的移动构造函数:
对内置类型按照内置类型的字节序拷贝
对自定义类型则先看这个类型是否实现了移动构造,实现了就用移动构造,否则用拷贝构造
namespace yyh
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 移动构造string(string&& s){swap(s);cout << "string(string&& s) -- 移动构造" << endl;}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动赋值string& operator=(string&& s){swap(s);cout << "string& operator=(string&& s) -- 移动赋值" << endl;return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;};
}class A
{
public:
private:int _a = 1;yyh::string _s;
};


1.3 移动赋值重载函数
如果没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器就会自动生成一个默认的移动赋值重载函数。
而编译器生成的移动赋值对内置类型和自定义类型的处理方法跟上面一样。


但是只要我们加上了析构函数 、拷贝构造、拷贝赋值重载中的任意一个。编译器就不会默认生成。
class A
{
public:~A() {}
private:int _a = 1;yyh::string _s;
};

1.4 成员变量缺省值
class A
{
public:~A() {}
private:int _a = 1;// 缺省值yyh::string _s = "aaa";// 缺省值
};
这里的缺省值会在构造/拷贝构造的初始化列表使用:
如果在构造/拷贝构造有这两个的初始化就不会走缺省值,谁不在初始化列表初始化就走谁的缺省值。
1.5 强制生成默认函数的关键字default
上面我们举例子写了析构函数后编译器就不会默认生成移动构造和移动赋值。
但是如果我们必须要写析构,有没有什么办法让编译器默认生成呢?
答案是可以使用default关键字。

class A
{
public:A(int a = 1, const char* s = ""): _a(a), _s(s){}// 只有移动构造没有拷贝构造会匹配歧义A& operator=(const A& aa) = default;A& operator=(A&& aa) = default;A(A&& a) = default;~A() {}
private:int _a = 1;yyh::string _s;
};int main()
{A a;A b;A c;// 无defaultA d(move(b));c = move(a);return 0;
}

1.6 禁止生成默认函数的关键字delete
1.6.1 C++98防拷贝
我们怎么让一个类对象不能被其他类拷贝呢?
首先我们能想到把拷贝构造写成私有,这样在外部就无法被拷贝。
但是如果有一个内部成员函数会调用拷贝函数怎么办?
在C++98中使用的方法是只声明不定义:
class A
{
public:A(){}// 只声明不定义A(const A& aa);~A(){delete[]_p;}private:int* _p = new int[10];
};int main()
{A a;A b(a);return 0;
}

1.6.1 C++11防拷贝
C++11是使用delete关键字来防止拷贝
class A
{
public:A(){}A(const A& aa) = delete;~A(){delete[]_p;}private:int* _p = new int[10];
};int main()
{A a;A b(a);return 0;
}

二、lambda表达式
2.1 对比
struct gift
{std::string _name;// 名称int _vol;// 体积double _val;// 价值gift(const char* name, int vol, double val): _name(name), _vol(vol), _val(val){}
};int main()
{std::vector<gift> v = { {"苹果", 10, 20.0}, {"梨子", 15, 12.8}, {"香蕉", 11, 10.2} };// 比较return 0;
}
当我们分别想按照名称、体积、价格来进行排序的时候,我们一般会写三个仿函数。
struct CmpNameLess
{bool operator()(const gift& g1, const gift& g2){return g1._name < g2._name;}
};struct CmpVolLess
{bool operator()(const gift& g1, const gift& g2){return g1._vol < g2._vol;}
};struct CmpValLess
{bool operator()(const gift& g1, const gift& g2){return g1._val < g2._val;}
};
我们可以看到这样会写很多的仿函数。我们可能遇到过这种代码:
int main()
{std::vector<gift> v = { {"苹果", 10, 20.0}, {"梨子", 15, 12.8}, {"香蕉", 11, 10.2} };//sort(v.begin(), v.end(), CmpNameLess());sort(v.begin(), v.end(), [](const gift& g1, const gift& g2){return g1._name < g2._name; });return 0;
}
这里就叫lambda表达式。
2.2 lambda表达式语法
lambda表达式书写格式:
[capture-list](parameters)mutable->return-type{statement}
说明:
[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
(parameters): 参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
mutable: 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
->returntype: 返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
{statement}: 函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
明白了这些语法我们就可以写一个比较大小的lambda表达式:
[](int x, int y)->bool {return x < y; };
[](int x, int y){return x < y; };
而lambda的本质是一个可调用的对象,我们只能用auto接收。可以像仿函数一样调用。
int main()
{// [](int x, int y)->bool {return x < y; };auto cmp = [](int x, int y){return x < y; };cout << cmp(1, 2) << endl;return 0;
}
2.3 捕捉列表
[]捕捉列表:
[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
[&var]:表示引用传递捕捉变量var
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针
这几个都可以在捕捉列表里混合使用,例如:
auto fun = [=, &b]() {};// 所有对象都用传值捕捉,只有b用引用捕捉
我们可以使用lambda来捕获上面定义过的变量。
int main()
{int a = 1, b = 2;auto add = [a](int x) {return x + a; };cout << add(b);return 0;
}
mutable:
当我们想要交换两个变量时:
int main()
{int a = 1, b = 2;auto Swap = [a, b](){int tmp = a;a = b;b = tmp;};return 0;
}

所以这里我们要加上mutable,让传进来的参数可以被修改。
int a = 1, b = 2;
auto Swap = [a, b]()mutable
{int tmp = a;a = b;b = tmp;
};
Swap();
但是我们调试发现a和b的值都没有被改变,原因是捕获列表默认是传值,并不会改变原来的值。
int main()
{int a = 1, b = 2;auto Swap = [&a, &b](){int tmp = a;a = b;b = tmp;};Swap();cout << a << " " << b << endl;return 0;
}
当然也可以使用[&]引用传递所有内容
auto Swap = [&]()
{int tmp = a;a = b;b = tmp;
};
这里就可以看出来mutable没有什么用处。
2.4 函数对象与lambda表达式
struct Add
{int operator()(int x, int y){return x + y;}
};int main()
{// 函数对象Add sum1;sum1(1, 2);// lambda表达式auto sum2 = [](int a, int b) {return a + b; };sum2(1, 2);return 0;
}

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()
相关文章:
【C++】C++11新特性——类的改进|lambda表达式
文章目录一、类的改进1.1 默认生成1.2 移动构造函数1.3 移动赋值重载函数1.4 成员变量缺省值1.5 强制生成默认函数的关键字default1.6 禁止生成默认函数的关键字delete1.6.1 C98防拷贝1.6.1 C11防拷贝二、lambda表达式2.1 对比2.2 lambda表达式语法2.3 捕捉列表2.4 函数对象与l…...
C语言进阶(37) | 程序环境和预处理
目录 1.程序的翻译环境和执行环境 2.详解编译链接 2.1 翻译环境 2.2 编译本身也分为几个阶段: 2.3 运行环境 3.预处理详解 3.1预定符号 3.2 #define 3.3 #undef 3.4 命令行定义 3.5 条件编译 3.6 文件包含 了解重点: 程序的翻译环境程序的执行环境详解: C语言程…...
Golang每日一练(leetDay0005)
目录 13. 罗马数字转整数 Roman to Integer ★ 14. 最长公共前缀 Longest Common Prefix ★ 15. 三数之和 3Sum ★★★ 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 13. 罗马数字转…...
occt_modeling_data(一)——拓扑
下面是我基于opencascade英文文档中关于occt_modeling_data中Topology部分进行的翻译,英文好的还是建议直接看文档,部分我不肯定的地方我会附上英文原句。如发现有错误欢迎评论区留言。 OCCT Topolog允许用户访问和操纵物体的数据,且不需要处…...
【AcWing】蓝桥杯备赛-深度优先搜索-dfs(3)
目录 写在前面: 题目:93. 递归实现组合型枚举 - AcWing题库 读题: 输入格式: 输出格式: 数据范围: 输入样例: 输出样例: 解题思路: 代码: AC &…...
宇宙最强-GPT-4 横空出世:最先进、更安全、更有用
文章目录前言一、准确性提升1.创造力2.视觉输入3.更长的上下文二、相比于ChatGPT有哪些提升1.GPT-4 的高级推理能力超越了 ChatGPT2.GPT-4 在多种测试考试中均优于 ChatGPT。三、研究团队在GPT-4模型都做了哪些改善1.遵循 GPT、GPT-2 和 GPT-3 的研究路径2.我们花了 6 个月的时…...
HashMap的实际开发使用
目 录 前言 一、HashMap是什么? 二、使用步骤 1.解析一下它实现的原理 编辑 2.实际开发使用 总结 前言 本章,只是大概记录一下hashMap的简单使用方法,以及理清一下hashMap的put方法的原理,以及get方法的原理。 一、Has…...
OpenCV入门(十三)快速学会OpenCV 12 图像梯度
OpenCV入门(十三)快速学会OpenCV 12 图像梯度1.Sobel算子1.1 计算x1.2 计算y1.3 计算xy2.Scharr算子2.1 计算x2.2 计算y2.3 计算xy3.Laplacian算子4.总结图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化较大,梯…...
软考:常见小题目计算题
01采购合同的类型采购合同主要包括总价类合同、成本补偿类合同、工料合同三大类合同。1、总价类合同此类合同为既定产品、服务或成果的采购设定一个总价。这种合同应在已明确定义需求,且不会出现重大范围变更的情况下使用。包括:(1࿰…...
【Linux】进程的程序替换
文章目录1. 程序替换1.创建子进程的目的是什么?2.了解程序是如何进行替换的3. 程序替换的基本原理当创建进程的时候,先有进程数据结构,还是先加载代码和数据?程序替换是整体替换,不是局部替换execl 返回值4. 替换函数1…...
【C++】模板(上)
文章目录1、泛型编程2、函数模板函数模板的实例化模板参数的匹配原则3、 类模板类模板的实例化1、泛型编程 void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& left, double& right) {double temp left;left …...
express框架利用formidable上传图片
express框架,在上传图片功能方面,用formidable里面的incomingform功能,很方便。很多功能都已经封装好了,非常好用,简单,不需要写更深层次的代码了。确实不错。 下面是我自己跟着黑马教程的博客系统的部分&…...
测试背锅侠?入职软件测试后大d佬给我丢了这个bug分类分析,至今受益匪浅......
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 刚成为入职…...
STM32 OTA应用开发——通过内置DFU实现USB升级(方式1)
STM32 OTA应用开发——通过内置DFU实现USB升级(方式1) 目录STM32 OTA应用开发——通过内置DFU实现USB升级(方式1)前言1 硬件介绍2 环境搭建2.1 Keil uVsion2.2 zadig2.3 STM32CubeProgrammer2.4 安装USB驱动3 OTA升级结束语前言 …...
基于MFC的JavaScript进行网页数据交互
目录 前言 一、创建html对话框工程 二、使用步骤 1.引入JavaScript接口代码 2.重写相关接口 3.在html网页中添加C/C调用的接口 4.在MFC工程中添加调用接口 5.设置确认按键触发调用 6.运行结果 总结 前言 如何快速的进行MFC开发,这里我介绍一种JavaScript与C/C交互的…...
AUTOSAR-Fee
Fee模块 全称Flash EEPROM Emulation Module,属于ECU抽象层 Fee模块本身是脱离硬件的,但是Fee模块可能会引用的Fls模块定制API,所以只能算半抽象. FEE模块应从设备特定的寻址方案和分段中抽象出来,并为上层提供虚拟寻址方案和分段(virtual addressing scheme and segment…...
Linux基本命令——操作演示
Linux基本命令——操作演示Linux的目录结构Linux命令入门目录切换相关命令(cd/pwd)相对路径、绝对路径和特殊路径符创建目录命令(mkdir)文件操作命令part1 (touch、cat、more)文件操作命令part2 (cp、mv、rm)查找命令 …...
【Linux】目录和文件的权限
Linux中的权限有什么作用Linux权限管理文件访问者的分类文件类型和访问权限(事物属性)**文件权限值的表示方法**文件访问权限的相关设置方法chmodchownchgrpumaskumask使用 sudo分配权限目录的权限Linux中的权限有什么作用 Linux下有两种用户࿱…...
Unity 优化之Player Setting
Quality SettingPixel Light Count 使用前向渲染时最大像素光源数。也是性能关键。数量越大消耗越多。Texture Quality:贴图质量,可以选择Half Res,这样速度会更快,但是贴图质量会轻微下降。Anisotropic Textures 纹理各向异形Ant…...
Qt——通过一个简单的程序例程熟悉使用Qt Creator软件进行项目搭建的基本流程(新建项目、项目的文件组成、修改ui文件、编译运行与调试)
【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...
C++11 constexpr和字面类型:从入门到精通
文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...
当下AI智能硬件方案浅谈
背景: 现在大模型出来以后,打破了常规的机械式的对话,人机对话变得更聪明一点。 对话用到的技术主要是实时音视频,简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术,开发自己的大模型。商用方案多见为字节、百…...
