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

【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 文件包含 了解重点&#xff1a; 程序的翻译环境程序的执行环境详解: C语言程…...

Golang每日一练(leetDay0005)

目录 13. 罗马数字转整数 Roman to Integer ★ 14. 最长公共前缀 Longest Common Prefix ★ 15. 三数之和 3Sum ★★★ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 13. 罗马数字转…...

occt_modeling_data(一)——拓扑

下面是我基于opencascade英文文档中关于occt_modeling_data中Topology部分进行的翻译&#xff0c;英文好的还是建议直接看文档&#xff0c;部分我不肯定的地方我会附上英文原句。如发现有错误欢迎评论区留言。 OCCT Topolog允许用户访问和操纵物体的数据&#xff0c;且不需要处…...

【AcWing】蓝桥杯备赛-深度优先搜索-dfs(3)

目录 写在前面&#xff1a; 题目&#xff1a;93. 递归实现组合型枚举 - AcWing题库 读题&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 数据范围&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 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是什么&#xff1f; 二、使用步骤 1.解析一下它实现的原理 ​编辑 2.实际开发使用 总结 前言 本章&#xff0c;只是大概记录一下hashMap的简单使用方法&#xff0c;以及理清一下hashMap的put方法的原理&#xff0c;以及get方法的原理。 一、Has…...

OpenCV入门(十三)快速学会OpenCV 12 图像梯度

OpenCV入门&#xff08;十三&#xff09;快速学会OpenCV 12 图像梯度1.Sobel算子1.1 计算x1.2 计算y1.3 计算xy2.Scharr算子2.1 计算x2.2 计算y2.3 计算xy3.Laplacian算子4.总结图像梯度计算的是图像变化的速度。对于图像的边缘部分&#xff0c;其灰度值变化较大&#xff0c;梯…...

软考:常见小题目计算题

01采购合同的类型采购合同主要包括总价类合同、成本补偿类合同、工料合同三大类合同。1、总价类合同此类合同为既定产品、服务或成果的采购设定一个总价。这种合同应在已明确定义需求&#xff0c;且不会出现重大范围变更的情况下使用。包括&#xff1a;&#xff08;1&#xff0…...

【Linux】进程的程序替换

文章目录1. 程序替换1.创建子进程的目的是什么&#xff1f;2.了解程序是如何进行替换的3. 程序替换的基本原理当创建进程的时候&#xff0c;先有进程数据结构&#xff0c;还是先加载代码和数据&#xff1f;程序替换是整体替换&#xff0c;不是局部替换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框架&#xff0c;在上传图片功能方面&#xff0c;用formidable里面的incomingform功能&#xff0c;很方便。很多功能都已经封装好了&#xff0c;非常好用&#xff0c;简单&#xff0c;不需要写更深层次的代码了。确实不错。 下面是我自己跟着黑马教程的博客系统的部分&…...

测试背锅侠?入职软件测试后大d佬给我丢了这个bug分类分析,至今受益匪浅......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 刚成为入职&#xf…...

STM32 OTA应用开发——通过内置DFU实现USB升级(方式1)

STM32 OTA应用开发——通过内置DFU实现USB升级&#xff08;方式1&#xff09; 目录STM32 OTA应用开发——通过内置DFU实现USB升级&#xff08;方式1&#xff09;前言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命令入门目录切换相关命令&#xff08;cd/pwd&#xff09;相对路径、绝对路径和特殊路径符创建目录命令&#xff08;mkdir&#xff09;文件操作命令part1 (touch、cat、more)文件操作命令part2 (cp、mv、rm&#xff09;查找命令 …...

【Linux】目录和文件的权限

Linux中的权限有什么作用Linux权限管理文件访问者的分类文件类型和访问权限&#xff08;事物属性&#xff09;**文件权限值的表示方法**文件访问权限的相关设置方法chmodchownchgrpumaskumask使用 sudo分配权限目录的权限Linux中的权限有什么作用 Linux下有两种用户&#xff1…...

Unity 优化之Player Setting

Quality SettingPixel Light Count 使用前向渲染时最大像素光源数。也是性能关键。数量越大消耗越多。Texture Quality&#xff1a;贴图质量&#xff0c;可以选择Half Res&#xff0c;这样速度会更快&#xff0c;但是贴图质量会轻微下降。Anisotropic Textures 纹理各向异形Ant…...

Qt——通过一个简单的程序例程熟悉使用Qt Creator软件进行项目搭建的基本流程(新建项目、项目的文件组成、修改ui文件、编译运行与调试)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...