【C++】C++11新特性(一)

文章目录

  • 列表初始化
  • initializer_list
  • 左值引用和右值引用

列表初始化

在 C++98 中可以使用{}对数组或者结构体元素进行统一的列表初始值设定

struct Point
{int _x;int _y;
};
int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0; 
}

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用列表初始化时,可添加等号(=),也可不添加

列表初始化可以防止窄化转换。窄化转换是指可能导致数据丢失或精度降低的转换。例如,将一个浮点数转换为整数时,如果浮点数的小数部分非零,就会丢失小数部分的数据。如果尝试进行窄化转换,编译器会报错

一切皆可用列表初始化,可以省略等号

class A
{
public:A(int a):_a(a){}A(int a,int b):_a(a),_b(b){}
private:int _a;int _b;
}
A a1{1};
A a2{11,45};
//本质上就是多参数的隐式类型转换
//在C++98单参数隐式类型转换的基础上引入了多参数的隐式类型转换
int arr[]{12,34,56,78,90};

创建对象时也可以使用列表初始化方式调用构造函数初始化

本质是生成一个 A 的临时对象,然后将这个临时对象赋给变量,编译器优化后就变成了隐式类型转换,可以通过在类前面加上关键字 explicit 来阻止隐式类型转换

A& a1{12};//临时对象具有常性,A&对象不能引用
const A& a2{12};

构造生成一个临时对象,临时对象具有常性,可以赋值给const变量

A a1=11;//单参数的隐式类型转换
A a2={11,12};//多参数的隐式类型转换,实质上是构造了一个A对象,然后调用了拷贝构造传给a2对象

initializer_list

本质是个常量数组,里面只存有指向first和last的指针,因此32位下只有8字节,支持迭代器,因此可以遍历

统一容器初始化方式:标准容器(如std::vectorstd::list等)都支持使用std::initializer_list进行初始化。这使得容器的初始化方式更加统一和直观。也可以作为operator=的参数,这样就可以用大括号赋值

如果想要自定义类型支持列表初始化,就可以在自定义类型的构造函数中使用std::initializer_list

std::initializer_list是一个不可变的类型,即一旦创建,它的元素不能被修改。它提供了beginend函数来访问其中的元素,就像访问数组一样,但不提供修改元素的接口

vector(const T& x1);
vector(const T& x1,const T&x2);
...
vector(initializer_list<T> il);
//这个构造一劳永逸的解决了问题,不用一个个写构造
vector(initializer_list<T>& il)
{vector(std::initializer_list<T> il)//可以不用加引用,和const原因:这里的initializer_list本来要的就是浅拷贝//实质上是il的指针分别指向常量数组的第一个元素和常量数组的最后一个元素,不用拷贝,直接就构造了,因此加上const和引用对性能提升没有影响//{1,2,3,4,5,6,78,75}
{reserve(il.size());for (auto& e : il){push_back(e);}
}
}
vector<int> v1={1,5,6,8,6,4}//隐式类型转换
vector<int> v2({1,5,5,6,9,1})//构造函数
map<int,int> m1={{1,2},{2,3},{3,4}};
//实际上是生成了隐式类型转换成了pair对象,然后再使用initializer_list<pair>进行构造

中间实际生成了临时对象,然后拷贝构造,实质也是隐式类型转换

实质是pair多参数的隐式类型转换和initializer_list

decltype

与typeid类似,但decltype推断出的类型可以定义变量,也可以用来模板传参,而typeid不行,typeid 只是一个字符串,不能用于定义对象

list<int>::iterator it1;
cout << typeid(it1).name();
//结果:class std::_List_iterator<class std::_List_val<struct std::_List_simple_types<int> > >
typeid(it1).name() it2;//错误,typeid推出的类型不能用于定义变量,只是一个字符串
decltype(it1) it2;
cout << typeid(it2).name();
//结果:class std::_List_iterator<class std::_List_val<struct std::_List_simple_types<int> > >
//用于赋值的类型和原类型一致

decltype 的通常用于推导 auto 的类型

class A
{
public:T* func(){return new T;}private:int _a=10;
};
auto func()
{list<int> l;return l.begin();
}
auto i = func();
A<decltype(i)> a;

decltype 和 auto 的使用会增加代码的阅读难度

左值引用和右值引用

左值和右值的区分:

左值可以取地址,右值不能取地址

匿名对象不能取地址,是右值

表示数据的表达式,如字面常量,表达式返回值,函数返回值等都是右值

右值引用:给右值取别名,常见右值: 字面常量、表达式返回值,函数返回值

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值但是可以取它的地址

左值引用和右值引用都是给对象起别名

纯右值(内置类型的)

将亡值(自定义类型的)

string&& ref1 = string("123");
string&& ref2 = to_string(123);
int&& ref3 = 10;
int&& ref4=(x+y);

左值引用不可以给右值起别名,但const修饰的左值可以

右值引用不可以给左值起别名,但是可以给move以后的左值起别名

const int& leftref = 10;
int a = 10;
int&& rightref = move(a);

move 只是会将左值强转为右值,但并不会涉及到资源的分配,只是告诉编译器可以进行右值操作,允许将这个变量的资源进行分配

被 move 之后的左值,一旦被分配,则自己就不再拥有原来的资源,而是分配出去了,就像真正的右值一样

引用的意义是减小拷贝,提高效率

左值引用没有彻底解决这个问题,因为局部变量无法引用

移动构造:传入右值引用的构造,将传入的右值的资源剥夺后分配给要构造的对象,避免了拷贝造成的资源占用

如果是右值,那么直接把资源转移

可以直接返回局部本来要销毁的变量,不用拷贝构造

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己

string func()
{string s="12456";return ret;//return move(ret);编译器自动优化成右值
}
string ret=func();
//这里由于编译器优化,会将原本ret需要拷贝构造一个临时对象
//然后ret通过移动构造将这个临时对象的资源拿到,优化为直接进行移动构造
//相当于将ret强行识别为右值

纯右值: 内置类型,返回类型为非引用类型的函数调用或运算符表达式属于纯右值,lambda 表达式为纯右值, 因为表达式本身没有名字,本质是临时值,例如 42、a + b 或 func()(函数返回非引用类型)
将亡值: 自定义类型,返回类型为对象右值引用的表达式为将亡值,右值类对象的成员为将亡值,右值数组的成员为将亡值,标识一个具名对象,但该对象即将被销毁(如通过 std::move 转换)。它允许安全地移动资源,而非拷贝

移动语义允许高效地转移资源,避免不必要的复制,特别是对于大型或资源密集型的对象,当一个变量或者右值被移动后,资源将会被分配到其他位置,原来的变量或右值不能够访问

**移动赋值:**移动赋值运算符(operator=的移动赋值版本)用于将一个对象的资源转移到另一个已经存在的对象中。与移动构造函数类似,它的主要目的是高效地处理资源的转移,避免不必要的资源复制。

移动赋值运算符通常也期望右值引用作为参数。当有右值(如临时对象)出现在赋值表达式的右侧时,编译器会优先选择移动赋值运算符(如果定义了的话)来进行资源的转移。

如果想对左值进行移动赋值操作,可以像移动构造函数一样,使用std::move函数将左值转换为右值引用

不能被移动的左值

  1. 常量对象(const 左值)

移动操作需要修改源对象,常量禁止对象修改,尝试移动常量对象会调用拷贝而非移动

const std::string s = "Hello";
std::string s2 = std::move(s);
// 调用拷贝构造函数,s中的值还是Hello,说明没有移动,只是进行了拷贝
  1. 基本数据类型的对象(int,float,double)

没有动态资源,移动等同于拷贝

int a = 10;
int&& rightref = move(a);//a中的值还是10,说明只是拷贝,并没有移动
  1. 无移动操作的类对象

若类未定义移动构造函数/赋值运算符,或编译器未隐式生成,则移动会退化为拷贝

class NoMove {
public:NoMove(){cout << "constructor function" << endl;}NoMove(const NoMove&){cout << "copy constructor" << endl;} // 只有拷贝构造函数
};
int main()
{NoMove obj1;NoMove obj2 = std::move(obj); // 调用拷贝构造函数
}
  1. 移动操作被显式删除的类对象

若移动构造函数/赋值运算符被标记为= delete,尝试移动会引发编译错误

class DeletedMove {
public:DeletedMove(){}DeletedMove(DeletedMove&&) = delete;
};
DeletedMove dm1;
DeletedMove dm2 = std::move(dm); // 编译错误
  1. 包含不可移动成员的类对象

若类的成员或基类不可移动,隐式移动操作会被删除,导致只能拷贝

struct NonMovableMember {NonMovableMember(NonMovableMember&&) = delete;
};
class Wrapper {NonMovableMember m;
};
Wrapper w;
Wrapper w2 = std::move(w); // 隐式移动被删除,尝试调用拷贝构造函数
struct S { int i : 4; };
S s;
int x = std::move(s.i); // 实际拷贝位域的值

右值引用可以引用字面常量int&& a=5,这里的 a 实际上是 const int&& 类型,因此不能通过 a 修改这个值

右值引用还能引用表达式产生的临时对象,如,在函数调用返回一个临时对象时,这个临时对象是右值,可以用右值引用绑定它

  • 假设存在一个函数std::vector<int> createVector(),它返回一个std::vector<int>对象。可以这样使用右值引用:std::vector<int>&& v = createVector();。这里createVector返回的临时vector对象被右值引用v绑定
  • 另一个例子是算术表达式的结果,如int&& result = (3 + 5);。表达式(3 + 5)产生一个临时的int8,右值引用result绑定了这个临时值

在移动语义的场景下,右值引用用于引用那些即将被移动资源的对象,当一个对象要将自己的资源(如动态分配的内存)转移给另一个对象时,会使用右值引用

右值引用本身是左值,只有右值本身处理成左值,才能实现移动构造,因此右值引用的函数要将接收到的右值传给下一个函数时,要进行 move,才能调用下一个函数的 move 形式

如果右值引用的属性是右值,那么移动构造和移动赋值,要转移的语法逻辑是矛盾的,因为右值无法被改变(可以理解为右值默认自带 const 属性)因此右值引用本身要被处理成左值

std::vector<int> createVector() {std::vector<int> v = {1, 2, 3};return v;
}
std::vector<int> v2 = std::move(createVector());

这里使用 move 的原因,由于不确定编译器是否会优化,此时,即使返回值在理论上是右值,仍然可能会调用拷贝构造函数进行复制,因此需要使用 move 告诉编译器需要使用,移动语义来传递对象,而不是依赖可能会发生也可能不会发生的返回值优化,函数返回值虽然在语义上是右值,但在语法形式上可以被当作左值处理

移动拷贝对于效率的提升是针对于自定义类型的深拷贝的类,因为只有深拷贝的类才有移动函数

对于内置类型和浅拷贝自定义类型,没有移动函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/903201.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小黑享受思考心流: 73. 矩阵置零

小黑代码 class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""items []m len(matrix)n len(matrix[0])for i in range(m):for j in range(n):if not m…

精益数据分析(19/126):走出数据误区,拥抱创业愿景

精益数据分析&#xff08;19/126&#xff09;&#xff1a;走出数据误区&#xff0c;拥抱创业愿景 在创业与数据分析的探索之旅中&#xff0c;我们都渴望获取更多知识&#xff0c;少走弯路。今天&#xff0c;我依然带着和大家共同进步的想法&#xff0c;深入解读《精益数据分析…

循环神经网络RNN---LSTM

一、 RNN介绍 循环神经网络&#xff08;Recurrent Neural Network&#xff0c;简称 RNN&#xff09;是一种专门用于处理序列数据的神经网络&#xff0c;在自然语言处理、语音识别、时间序列预测等领域有广泛应用。 传统神经网络 无法训练出具有顺序的数据。模型搭建时没有考…

优考试V4.20机构版【附百度网盘链接】

优考试局域网考试系统具有强大的统计分析功能。优考试通过对考试数据进行统计分析&#xff0c;诸如考试分数分布&#xff0c;考试用时分布&#xff0c;错排行等&#xff0c;让你从整体上了解你的学员&#xff08;员工&#xff09;状态&#xff0c; 同时你也可以对学员&#xff…

【Amazing晶焱科技高速 CAN Bus 传输与 TVS/ESD/EOS 保护,将是车用电子的生死关键无标题】

台北国际车用电子展是亚洲地区重量级的车用电子科技盛会&#xff0c;聚焦于 ADAS、电动车动力系统、智慧座舱、人机界面、车联网等领域。各大车厂与 Tier 1 供应链无不摩拳擦掌&#xff0c;推出最新技术与创新解决方案。 而今年&#xff0c;“智慧座舱” 无疑将成为全场焦点&am…

面试:结构体默认是对齐的嘛?如何禁止对齐?

是的。 结构体默认是对齐的‌。结构体对齐是为了优化内存访问速度和减少CPU访问内存时的延迟。结构体对齐的规则如下&#xff1a; 某数据类型的变量存放的地址需要按有效对齐字节剩下的字节数可以被该数据类型所占字节数整除&#xff0c;char可以放在任意位置&#xff0c;int存…

如何优雅地解决AI生成内容粘贴到Word排版混乱的问题?

随着AI工具的广泛应用&#xff0c;越来越多人开始使用AI辅助撰写论文、报告或博客。然而&#xff0c;当我们直接将AI生成的文本复制到Word文档中时&#xff0c;常常会遇到排版混乱、格式异常的问题。这是因为大部分AI输出时默认使用了Markdown格式&#xff0c;而Word对Markdown…

Golang | HashMap实现原理

HashMap是一种基于哈希表实现的键值对存储结构&#xff0c;它通过哈希函数将键映射到数组的索引位置&#xff0c;支持高效的插入、查找和删除操作。其核心原理如下&#xff1a; 哈希函数&#xff1a;将键转换为数组索引。理想情况下&#xff0c;不同键应映射到不同索引&#xf…

vue3学习之防抖和节流

​ 在前端开发中&#xff0c;我们经常会遇到这样的情况&#xff1a;某些事件&#xff08;如滚动、输入、点击等&#xff09;会频繁触发&#xff0c;如果不加以控制&#xff0c;可能会导致性能问题。Vue3 中的防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&a…

4.2.2 MySQL索引原理以及SQL优化

文章目录 4.2.2 MySQL索引原理以及SQL优化1. 索引与约束1. 索引是什么2. 索引的目的3. 几种索引4. 约束1.外键2. 约束 vs 索引的区别 5. 索引实现1. 索引存储2. 页3. B树4. B树层高问题5. 自增id6. 聚集索引7. 辅助索引 8. innnodb体系结构1. buffer pool2. change buffer 9. 最…

【学习笔记】文件包含漏洞--本地远程包含、伪协议、加密编码

一、文件包含漏洞 和SQL等攻击方式一样&#xff0c;文件包含漏洞也是一种注入型漏洞&#xff0c;其本质就是输入一段用户能够控制的脚本或者代码&#xff0c;并让服务端执行。 什么叫包含呢&#xff1f;以PHP为例&#xff0c;我们常常把可重复使用的函数写入到单个文件中&…

蓝桥杯 2021年模拟赛 扫雷问题

题目&#xff1a; 在一个 n 行 m 列的方格图上有一些位置有地雷&#xff0c;另外一些位置为空。 请为每个空位置标一个整数&#xff0c;表示周围八个相邻的方格中有多少个地雷。 输入描述 输入的第一行包含两个整数 n,m。 第 22行到第n1 行每行包含 m 个整数&#xff0c;相…

写windows服务日志-.net4.5.2-定时修改数据库中某些参数

环境&#xff1a; windows 11 Visual Studio 2015 .net 4.5.2 SQL Server 目的&#xff1a; 定时修改数据库中某些参数的值 定时修改24小时内&#xff0c;SQL数据库中&#xff0c;表JD_Reports 内&#xff0c;如果部门是‘体检科&#xff0c;设置打印类型为 1 可以打印。步骤&a…

madvise MADV_FREE对文件页统计的影响及原理

一、背景 madvise系统调用是一个与性能优化强相关的一个系统调用。madvise系统调用包括使用madvise函数&#xff0c;也包含使用posix_fadvise函数。如我们可以使用posix_fadvise传入POSIX_FADV_DONTNEED来清除文件页的page cache以减少内存压力。 这篇博客里&#xff0c;我们…

于键值(KV)的表

基于键值&#xff08;KV&#xff09;的表 将行编码为键值&#xff08;KVs&#xff09; 索引查询&#xff1a;点查询和范围查询 在关系型数据库中&#xff0c;数据被建模为由行和列组成的二维表。用户通过SQL表达他们的意图&#xff0c;而数据库则神奇地提供结果。不那么神奇的…

2025年邵阳市工程技术研究中心申报流程、条件、奖补

一、邵阳市工程技术研究中心申报条件 &#xff08;一&#xff09;工程技术研究中心主要依托科技型企业组建&#xff0c;依托单位应具有以下条件&#xff1a; 1. 具有较强技术创新意识的领导班子和技术水平高、工程化实践经验丰富的工程技术研发队伍&#xff0c;其中固定人员…

Python+AI提示词出租车出行轨迹预测:梯度提升GBR、KNN、LR回归、随机森林融合及贝叶斯概率异常检测研究

原文链接&#xff1a;tecdat.cn/?p41693 在当今数字化浪潮席卷全球的时代&#xff0c;城市交通领域的海量数据如同蕴藏着无限价值的宝藏等待挖掘。作为数据科学家&#xff0c;我们肩负着从复杂数据中提取关键信息、构建有效模型以助力决策的使命&#xff08;点击文末“阅读原文…

系统重装——联想sharkbay主板电脑

上周给一台老电脑重装系统系统&#xff0c;型号是lenovo sharkbay主板的电脑&#xff0c;趁着最近固态便宜&#xff0c;入手了两块长城的固态&#xff0c;装上以后插上启动U盘&#xff0c;死活进不去boot系统。提示 bootmgr 缺失&#xff0c;上网查了许久&#xff0c;终于解决了…

python连接Elasticsearch并完成增删改查

python库提供了elasticsearch模块,可以通过以下命令进行快速安装,但是有个细节需要注意一下,安装的模块版本要跟es软件版本一致,此处举例:7.8.1 pip install elasticsearch==7.8.1 首先连接elasticsearch,以下是免密示例 from elasticsearch import Elasticsearch# El…

PDF嵌入图片

所需依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-core</artifactId><version>9.0.0</version><type>pom</type> </dependency>源码 /*** PDF工具*/ public class PdfUtils {/*** 嵌入图…