【C++11(一)】列表初始化and右值引用

news2024/10/7 18:27:31

在这里插入图片描述

一、 统一的列表初始化

1.1 {}初始化

在C++98中,标准允许
使用花括号{}对数组或者结构体元素
进行统一的列表初始值设定

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

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void test1()
{
	Date d1(2024, 1, 29); // old style
	// C++11支持的列表初始化,这里会调用构造函数初始化
	Date d2{ 2024, 1, 30 };
	Date d3 = { 2024, 1, 31 };
	vector<Date> vd1 = { d1, d2 };
	vector<Date> vd2 = { Date{ 2024, 1, 29 }, Date{ 2024, 1, 30 } };
	vector<Date> vd3 = { { 2024, 1, 29 }, { 2024, 1, 31 } };
	map<string, string> dict = { {"sort", "排序"}, {"string", "字符串"}, {"Date", "日期"} };
	pair<string, string> kv1 = { "Date", "日期" };
	pair<string, string> kv2{ "Date", "日期" };
}

1.2 std::initializer_list

std::initializer_list的介绍文档:
http://www.cplusplus.com/reference/initializer_list/initializer_list/
std::initializer_list是什么类型:

int main()
{
	// the type of il is an initializer_list 
	auto il = { 10, 20, 30 };
	cout << typeid(il).name() << endl;

	initializer_list<int>::iterator it = il.begin();
	cout << it;
	// *it = 1; // 不能修改因为指向常量区
 	return 0;
}

std::initializer_list使用场景:
std::initializer_list一般是作为构造函数的参数
C++11对STL中的不少容器就增加
std::initializer_list作为参数的构造函数
这样初始化容器对象就更方便了
也可以作为operator=的参数
这样就可以用大括号赋值

以前我们模拟实现的vector
不支持{}初始化和赋值
现在可以通过加一个构造函数实现
{}初始化和赋值

vector(initializer_list<T> il)
{
	// 第一种方法用迭代器支持
	/*typename initializer_list<T>::iterator it = il.begin();
	while (it != il.end())
	{
		push_back(*it);
		++it;
	}*/

	// 第二种方法范围for支持
	for (auto& e : il)
	{
		push_back(e);
	}
}

二、声明

2.1 decltype

关键字decltype将变量的类型
声明为表达式指定的类型

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
decltype(t1 * t2) ret;
cout << typeid(ret).name() << endl;
}
int main()
{
const int x = 1;
double y = 2.2;
decltype(x * y) ret; // x*y是double类型,ret的类型是double
decltype(&x) p;      // p的类型是const int*
cout << typeid(ret).name() << endl;
cout << typeid(p).name() << endl;
F(1, 'a');
return 0;
}

// 以上都能通过auto解决,而下面则无法通过auto解决
// vector存储的类型跟x*y表达式返回值类型一致
vector<decltype(x * y)> v;

decltype可以推导一个表达式的类型
用这个类型实例化模板参数或定义对象

2.2 nullptr

由于C++中NULL被定义成字面量0
这样就可能回带来一些问题
因为0既能指针常量
又能表示整形常量
所以出于清晰和安全的角度考虑
C++11中新增了nullptr,用于表示空指针

三、STL中一些变化

3.1 新容器

用橘色圈起来是
C++11中的一些几个新容器
最有用的是unordered_map和
unordered_set
array和forward_list就比较鸡肋
在这里插入图片描述
基本每个容器都增加了一些C++11的方法

  1. 增加支持initializer_list的构造函数
    使用更方便, 有一定价值
  2. 增加cbegin和cend系列迭代器接口
    比较鸡肋
  3. 移动构造和移动赋值
    vector (vector&& x);
  4. 支持右值引用相关插入接口函数

3和4价值非常大, 提高了拷贝效率

四、右值引用和移动语义

4.1 左值引用

我们之前学的引用叫左值引用
而C++11中新增了的右值引用语法特性
无论左值引用还是右值引用
都是给对象取别名

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

// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;
// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

4.2 右值引用

右值也是一个表示数据的表达式
如:字面常量、表达式返回值
函数返回值(这个不能左值引用返回)等等
右值只能出现在赋值符号的右边
右值不能取地址
右值引用就是对右值的引用,给右值取别名

double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);
// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;

需要注意的是:
右值不能取地址的
但是给右值取别名后
会导致右值被存储到特定位置
且可以取到该位置的地址
也就是说例如:不能取字面量10的地址
但是rr1引用后,可以对rr1取地址,也可以修改rr1
如果不想rr1被修改,可以用const int&& rr1 去引用

double x = 1.1, y = 2.2;
 int&& rr1 = 10;
 const double&& rr2 = x + y;
 rr1 = 20;
 rr2 = 5.5;  // 报错

4.3 左值引用与右值引用比较

左值:

  1. 左值引用只能引用左值,不能引用右值
    (权限会被放大)
  2. 但是const左值引用既可引用左值
    也可引用右值
 // 左值引用只能引用左值,不能引用右值。
    int a = 10;
    int& ra1 = a;   // ra为a的别名
    //int& ra2 = 10;   // 编译失败,因为10是右值
    // const左值引用既可引用左值,也可引用右值。
    const int& ra3 = 10;
    const int& ra4 = a;

右值:

  1. 右值引用只能右值,不能引用左值
  2. 但是右值引用可以move以后的左值
// 右值引用只能右值,不能引用左值。
 int&& r1 = 10;
 
 // error C2440: “初始化”: 无法从“int”转换为“int &&”
 // message : 无法将左值绑定到右值引用
 int a = 10;
 int&& r2 = a;
 // 右值引用可以引用move以后的左值
 int&& r3 = std::move(a);

std::move的功能:
将一个左值强制转化为右值(将亡值)
(但并不能移动任何东西)
然后通过右值引用使用该值,以用于移动语义
从实现上讲
std::move基本等同于一个类型转换:
static_cast<T&&>(lvalue);
C++ 标准库使用比如vector::push_back等这类函数时
会对参数的对象进行复制,连数据也会复制
这会造成对象内存的额外创建
本来只是想把参数push_back进去就行了
通过std::move,可以避免不必要的拷贝操作
std::move是为性能而生
std::move是将对象的状态或者所有权
从一个对象转移到另一个对象
只是转移,没有内存的搬迁或者内存拷贝

4.4 右值引用使用场景和意义

右值分为纯右值和将亡值

// 模拟实现string
string s1("hello world");
// 如果s1是左值,调用正常拷贝构造走深拷贝
string ret1 = s1;

// 如果是右值(将亡值)还走深拷贝就很不划算
// 将亡值是即将被delete的值
string ret2 = (s1+'!');

这时可以重新写一个拷贝构造
用右值引用来接收传参
以此区分左值和右值
如果是右值把我置空跟你交换一下
在这里插入图片描述
并且在函数传参上右值引用也能发挥作用

string to_string(int value)
{
	string ret;
	return ret; 
}

int main()
{
	string s = to_string(123);
	return 0;
}

返回值把值赋给临时对象有一次拷贝构造
临时对象把值赋给s又会有一次拷贝构造
编译器优化之后把值直接赋给s
减少一次拷贝构造

有了右值引用
在这里插入图片描述
这样ret返回值不用调析构
s也不用调拷贝

总结:
左值引用减少拷贝, 提高效率
右值引用也是减少拷贝, 提高效率
但是他们的角度不同
左值引用是直接减少拷贝
右值引用是间接减少拷贝
识别出是左值还是右值
如果是右值, 则不再深拷贝
直接移动拷贝, 提高效率

五、模板中的&& 万能引用

模板中的&&不代表右值引用
而是万能引用,其既能接收左值又能接收右值
模板的万能引用只是提供了
能够接收同时接收左值引用和右值引用的能力

void Fun(int &x){ cout << "左值引用" << endl; }
void Fun(const int &x){ cout << "const 左值引用" << endl; }
void Fun(int &&x){ cout << "右值引用" << endl; }
void Fun(const int &&x){ cout << "const 右值引用" << endl; }

template<typename T>
void PerfectForward(T&& t)
{
 Fun(t);
}
int main()
{
 PerfectForward(10);           // 右值
 int a;
 PerfectForward(a);            // 左值
 PerfectForward(std::move(a)); // 右值
 const int b = 8;
 PerfectForward(b);      // const 左值
 PerfectForward(std::move(b)); // const 右值

但是引用类型的唯一作用就是限制接收的类型
后续使用中都退化成了左值
如果希望在传递过程中保持它的
左值或者右值的属性
就需要用到完美转发

5.1 完美转发

std::forward 完美转发
在传参的过程中保留对象原生类型属性

template<typename T>
void PerfectForward(T&& t)
{
 Fun(std::forward<T>(t));
}
int main()
{
 PerfectForward(10);           // 右值
 int a;
 PerfectForward(a);            // 左值
 PerfectForward(std::move(a)); // 右值
 const int b = 8;
 PerfectForward(b);      // const 左值
 PerfectForward(std::move(b)); // const 右值

在这里插入图片描述
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见

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

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

相关文章

bash脚本学习笔记

一、扫盲 脚本文件是一种文本文件&#xff0c;其中包含了一系列的命令和指令&#xff0c;可以被操作系统解释器直接解释执行。脚本文件通常被用来完成特定的任务或执行重复性的操作。 脚本文件通常以某种编程语言的语法编写&#xff0c;例如 Bash、Python、Perl、Ruby 等等。…

IDEA JDBC配置

一、在pom中添加依赖 <dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency></dependencies> 然后同步一下 二、编写代码…

Linux 系统服务

一、CentOS 6 与 CentOS 7开机流程 1.CentOS 6 1.1.打开电源首先通过内核引导开机。 1.2.开机自检&#xff0c;加载 BIOS 硬件信息。 1.3.MBR 记录一般是在磁盘0磁道0扇区&#xff0c;共512个字节。前446个字节是 BootLoder。计算机不知道我们的系统在哪里&#xff0c;所以需…

【Java数据结构】模拟实现ArrayList

import java.util.Arrays;/*** ArrayList的模拟实现*/ class SeqList{private int[] elem;private int usedSize0;//记录当前顺序表有多少个有效数字public static final int DEFAULT_CAPACITY10;//默认大小是10public SeqList(){this.elemnew int[DEFAULT_CAPACITY];}//新增元…

基于FFT + CNN -Transformer时域、频域特征融合的电能质量扰动识别模型

目录 往期精彩内容&#xff1a; ​模型整体结构 1 快速傅里叶变换FFT原理介绍 第一步&#xff0c;导入部分数据&#xff0c;扰动信号可视化 第二步&#xff0c;扰动信号经过FFT可视化 2 电能质量扰动数据的预处理 2.1 导入数据 2.2 制作数据集 3 基于FFTCNN-Transform…

服务攻防-端口协议桌面应用QQWPS等RCEhydra口令猜解未授权检测

知识点&#xff1a; 1、端口协议-弱口令&未授权&攻击方式等 2、桌面应用-社交类&文档类&工具类等 章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数据库&中间件&…

AS-V1000产品介绍:支持GA/T1400视图库标准(可通过GA/T1400接入海康、华为、大华等图传前端设备,实现图传功能)

目 录 一、概述 二、AS-V1000视频监控管理平台的特点 二、视频监控平台通过GA/T1400接入前端设备 &#xff08;一&#xff09;接入华为GA/T1400前端设备 &#xff08;二&#xff09;接入大华GA/T1400前端设备 &#xff08;三&#xff09;接入海康威视GA/…

QML ListView 列表视图

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 虽然 Repeater 在重复创建多个相似项的时候很方便,但是通常只适用于有限的简单元素,并且它还无法滚动浏览。而基于 Flickable 的视图组件(如 GridView、ListView、TableView、TreeView 等)则弥补了这些缺…

vue-3d-model

vue-3d-model - npm GitHub - hujiulong/vue-3d-model: &#x1f4f7; vue.js 3D model viewer component 通过该插件降低Threejs的使用难度 vue项目加载三维模型&#xff0c;我把模型放在了服务器的tomcat里面&#xff0c;需要对tomcat的fbx项目文件夹设置跨域&#xff0c;如…

Docker容器引擎(5)

目录 一.docker-compose docker-compose的三大概念&#xff1a; yaml文件格式&#xff1a; json文件格式&#xff1a; docker-compose 配置模板文件常用的字段&#xff1a; 二.Docker Compose 环境安装&#xff1a; 查看版本&#xff1a; 准备好nginx 的dockerfile的文…

微信小程序(三十一)本地同步存储API

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.存储数据 2.读取数据 3.删除数据 4.清空数据 源码&#xff1a; index.wxml <!-- 列表渲染基础写法&#xff0c;不明白的看上一篇 --> <view class"students"><view class"item…

银行数据仓库体系实践(16)--数据应用之财务分析

总账系统 在所有公司中&#xff0c;财务分析的基础都是核算&#xff0c;那在银行的系统体系中&#xff0c;核算功能在业务发生时由业务系统如核心、贷款、理财中实现登记&#xff0c;各业务系统会在每天切日后统计当天各机构的核算科目的发生额与余额&#xff0c;并统一送到总账…

k8s二进制及负载均衡集群部署详解

目录 常见部署方式 二进制部署流程 环境准备 操作系统初始化配置 关闭防火墙 配置SELinux 关闭SWAP 根据规划设置主机名 在master添加hosts&#xff0c;便于主机名解析 调整内核参数 配置时间同步 部署docker引擎 在所有node节点部署docker引擎 部署etcd集群 签发…

shell脚本自动备份数据库表

今日目标&#xff1a;shell脚本自动备份数据库中的表并记录执行日志和mysql输出日志 编写思路&#xff1a; &#xff08;1&#xff09;shell脚本运行mysql命令 &#xff08;2&#xff09;脚本输出记录到日志中 &#xff08;3&#xff09;定时任务自动执行shell脚本 1、she…

王子公主来扫雷0^0

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 今日主菜&#xff1a;扫雷小游戏&#xff0c; 主厨&#xff1a;邪王真眼 所属专栏&#xff1a; C语言知识点 主厨的主页&#xff1a;Chef‘s blog 引言&a…

SpringFramework实战指南(五)

SpringFramework实战指南(五) 4.3 基于 注解 方式管理 Bean4.3.1 实验一: Bean注解标记和扫描 (IoC)4.3.2 实验二: 组件(Bean)作用域和周期方法注解4.3.3 实验三: Bean属性赋值:引用类型自动装配 (DI)4.3.4 实验四: Bean属性赋值:基本类型属性赋值 (DI)4.3.5 实验五:…

C语言第十六弹---操作符(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 操作符 1、下标访问[]、函数调用() 1.1、[ ] 下标引用操作符 1.2、函数调用操作符 2、结构成员访问操作符 2.1、结构体 2.1.1、结构的声明 2.1.2、结构体变…

Rust学习之Features

Rust学习之Features 一 什么是 Features二 默认 feature三 简单的features应用示例四 可选(optional)的依赖五 依赖的特性5.1 在依赖表中指定5.2 在features表中指定 六 命令行中特性控制七 特性统一路径八 其它8.1 相互排斥特性8.2 观察启用特性8.3 [Feature resolver version…

【Java程序设计】【C00234】基于Springboot的美食生活分享平台(有论文)

基于Springboot的美食生活分享平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的美食生活分享平台 主要功能如下&#xff1a;用户模块管理、美食分享笔记管理、美食笔记详情管理、公告信息管理、用户评论模块管…

node 第二十四天 mongoDB shell 命令 高级方法 $where aggregate聚合

$where 数据库数据如下 使用where语法如下 等价于 2.aggregate 聚合 使用聚合管道执行聚合操作。该管道允许用户通过一系列基于阶段的操作来处理来自集合或其他源的数据。 过滤数据, 分组数据 (排除name为 AAA 的数据 按price进行分组 每匹配一组计数1) 下面我们用aggregate…