【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开发实战》...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...