【C++11】左值引用 与 右值引用

news2024/11/24 4:09:11

定义

左值 / 左值引用

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

左值引用:就是给左值的引用,给左值取别名。

例子:

// 变量 a, b、指针变量 p 和解引用表达式 *p 都是左值
	int* p = new int(0);
	int a = 1;
	const int b = 2;

// 下面是对上述左值的引用
	int*& rp = p;
	int& ra = a;
	const int& rb = b;
	int& pValue = *p;

右值 / 右值引用

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

右值引用:就是对右值的引用,给右值取别名

double x = 1.0, y = 2.5;
// 几种常见的右值
10;
x + y;
fmax(x, y);
// cout << &(x + y) << endl; //对右值不能进行取地址

//以下是对上述右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmax(x, y);

左值引用 / 右值引用 特点比较

左值引用:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值
// 左值引用只能引用左值,不能引用右值
	int a = 10;
	int& ra1 = a;
	int& ra2 = 10; //编译失败,10是右值

// const左值引用既可以引用左值,也可以引用右值
	const int& ra3 = a;
	const int& ra4 = 10; // 不报错

右值引用:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。
// 右值引用只能引用右值,不能引用左值
	int&& r1 = 10;

	int a = 10;
	int&& r2 = a; //编译失败: 无法将右值引用绑定到左值

// 右值引用可以引用 move 后的左值
	int&& r3 = std::move(a);

右值引用的应用场景 和 意义

上文了解到 const左值引用 可以引用右值,既然如此,右值引用的意义是什么?
实际上,左值引用有一定的短板,而右值引用可以弥补该缺陷:

首先看下面的代码:

namespace aiyimu
{
	class string
	{
	public:
		// 构造函数
		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); //std::swap -> ::swap
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) <---> 拷贝构造(深拷贝)" << endl;
			// 拷贝操作
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

		// 移动构造
		// 以右值引用作参数
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) <---> 资源转移" << endl;
			swap(s);
		}
		
		// 拷贝赋值运算符
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) <---> 拷贝赋值(深拷贝)" << endl;
			string tmp(s);
			swap(tmp);

			return *this;
		}

		// 移动赋值运算符
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) <---> 移动赋值(资源移动)" << endl;
			swap(s);

			return *this;
		}		

		// 析构
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

移动构造函数

定义:

移动构造(Move Constructor)是一种特殊的构造函数,它通过接收一个右值引用参数来创建新对象,并从传入的对象中“移动”资源而不是执行深拷贝。

详解:

我们在代码实现中 写了移动构造函数,与右值引用有一定的联系,详解在这里:

移动构造函数


引用构造 的博客中有具体写到:

  • 由于我们实现的 拷贝构造 的参数是const 类型,所以既可以进行左值引用也可以进行右值引用
  • 当存在移动构造时,传入右值优先调用移动构造,否则构造此时的拷贝构造。
  • 对上面的代码,to_string 的 参数是 右值,调用移动构造,但我们讲两种构造都作讨论。

认识了移动构造函数后,对下面的代码:

aiyimu::string to_string(int value)
{
	aiyimu::string str;

	// to_string的内容并不重要 
	// ... ... 这里省略
	
	return str;
}

int main()
{
	aiyimu::string ret("114");
	ret = to_string(514);
	return 0;
}

当执行的是拷贝构造+拷贝赋值时:

在这里插入图片描述

可以看出拷贝构造时 进行了两次深拷贝

而当执行的是移动构造+移动赋值时:

在这里插入图片描述

此时的右值引用 避免了两次深拷贝,避免了空间浪费。

可以看出 右值引用 配合 移动构造和移动赋值 的作用。


万能引用(引用折叠)

万能引用(Universal Reference)是由C++中的std::forward和模板推导机制引入的概念。它是一种特殊类型的引用,可以接受各种类型的参数(包括左值、右值)。

定义

首先,在C++中,几乎所有的标准 容器类都支持万能引用 的用法。

  1. 如果传递给万能引用的是一个左值,那么万能引用将被推导为左值引用。这意味着它将保留传递给它的参数的左值特性,并且不能绑定到临时对象或右值。

例如:

int x = 42;
int& lvalueRef = x;       // 左值引用
foo(lvalueRef);           // T 被推导为 int&
  1. 如果传递给万能引用的是一个右值,那么万能引用将被推导为普通的右值引用。这意味着它可以绑定到临时对象、右值或将对象视为右值的情况下。

例如:

int&& rvalueRef = 100;    // 普通的右值引用
foo(std::move(rvalueRef)); // T 被推导为 int&&

请注意,万能引用只能 通过模板来声明 。在函数模板中使用万能引用的时候,根据传递的参数类型和值类别,编译器会进行相应的类型推导。


在这里插入图片描述
在这里插入图片描述

查阅发现,C++11 在list类中添加了万能引用的实现。

声明

万能引用的格式是使用&&语法来声明,它的一般形式为:

template<typename T>
void foo(T&& arg) {
    // 根据 arg 的值类别进行不同的处理
    if constexpr (std::is_lvalue_reference_v<T>) {
        // 处理左值
        std::cout << "左值引用" << std::endl;
    } else {
        // 处理右值
        std::cout << "右值引用" << std::endl;
    }
}

用法

下面的代码展示万能引用的用法:

// 万能引用
void Func(int& x) { cout << "左值引用" << endl; }
void Func(const int& x) { cout << "const 左值引用" << endl; }

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

template<typename T>
void PerfectForward(T&& t)
{
	// 完美转发: 保持t引用对象属性
	Func(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 右值

	aiyimu::list<aiyimu::string> lt;
	aiyimu::string s1("hello");
	lt.push_back(s1);

	cout << endl;

	//lt.push_back(aiyimu::string("world"));
	lt.push_back("world");

	return 0;
}

在这里插入图片描述
根据执行结果,可以验证万能引用的作用。

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

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

相关文章

青少年机器人技术一级核心知识点:机械结构及模型(二)

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

持续集成工具Jenkins安装和部署

前言 Jenkins的执行流程图如下&#xff1a; 1. 前期准备 1.1 安装JDK 目前新版本的Jenkins对JDK的要求基本上都在JDK11以上&#xff0c;所以我这边将我服务器的JDK版本升级成为JDK11。 具体升级步骤如下&#xff1a; 下载安装包 官网可能需要注册账号&#xff0c;这里我…

微信:如何查询自己名下已实名认证绑定的几个微信账户?

你知道如何查询自己名下已实名认证绑定了几个微信账户吗&#xff1f;微信规则同一个人最多可以注册绑定完成5个微信账户认证&#xff0c;如果想注册新微信号&#xff0c;必须保证实名认证微信账户不足5个。而且通过查询自己名下实名认证微信账户还可以确认&#xff0c;自己的身…

LINUX系统(ubuntu)安装以及应用调试(不定时更新)

一&#xff1a;linux的介绍 Linux是一种基于UNIX操作系统的开源&#xff08;Open Source&#xff09;操作系统。它由芬兰计算机科学家 Linus Torvalds 在1991年首次发布&#xff0c;目前已经发展成为最流行和广泛使用的操作系统之一。 Linux以其稳定性、安全性和灵活性而闻名…

07-C++学习笔记-函数

&#x1f4da; 函数的概念 函数是一段可重复使用的代码块&#xff0c;用于完成特定的任务。通过使用函数&#xff0c;可以将程序划分为多个模块&#xff0c;提高代码的可读性、可维护性和复用性。 在C中&#xff0c;函数由函数头和函数体组成。函数头包含函数的返回类型、函数…

日期格式化不起作用 2022-09-18T05:25:30.000+00:00

java->web JsonFormat(pattern “yyyy-MM-dd HH:mm:ss”)一般版本问题或依赖冲突不起作用 解决&#xff1a; spring:jackson:serialization:write-dates-as-timestamps: falsedate-format: yyyy-MM-dd HH:mm:ss这个配置会在java对象传输给web前端的时候对日期的字段进行…

Linux 查看端口占用命令

文章目录 1、lsof -i:端口号2、netstat 命令2.1 netstat -tunlp 命令2.2 netstat -anp 命令 1、lsof -i:端口号 用于查看某一端口的占用情况&#xff0c;比如查看5000端口使用情况&#xff1a; lsof -i:5000常用命令&#xff1a; lsof -i:5000&#xff1a;查看5000端口占用 …

QT或VS2015报错:Error: C2661: QColor::ct::ct: 没有重载函数接受 5 个参数解决方案

安装了QT5.14.2 MSCV2015配置并同时在QT或VS2015测试并运行都提示没有重载函数接受 5 个参数。 同时还会出现C2134错误&#xff1a;QMetaObject::SuperData::operator const QMetaObject *: 调用不会生成常数表达式的错误 搜索了网络上的结果都让换其它版本&#xff0c;没有…

高压线路零序电流方向保护程序逻辑原理(一)

一、微机型零序电流方向保护概念 &#xff08;一&#xff09;保护电流元件的配置 零序电流方向保护是反应大接地电流系统的线路发生接地故障时&#xff0c;零序电流分量大小和方向的多段式电流方向保护。在我国大接地电流系统线路上都装设了这种接地保护装置&#xff0c;这种保…

(推荐)Abaqus中C++子程序开发入门

ABAQUS是支持C子程序开发的&#xff0c;相比于传统的Fortran&#xff0c;C作为高级语言的优势不言而喻&#xff0c;再搭配优秀的C程序库&#xff0c;使得我们的编程效率大大提高&#xff0c;尤其是对于熟悉C编程的开发者&#xff0c;不失为一种更好的选择。 1 软件配置 1.1 环…

20230702 正态分布的几个性质

正态分布以及高斯函数的定义 如果随机变量 X X X 的密度函数为 f μ , σ ( x ) 1 σ 2 π e − ( x − μ ) 2 2 σ 2 , x ∈ R , σ > 0 f_{\mu, \sigma}(x)\frac{1}{\sigma \sqrt{2 \pi}} e^{-\dfrac{(x-\mu)^2}{2 \sigma^2}}, \quad x \in \mathbb{R}, \sigma>0 …

the size of an array must be greater than zero

keil默认不支持数组定义的情况buf[0]

Pytorch深度强化学习1-2:详解K摇臂赌博机模型和ϵ-贪心算法

目录 0 专栏介绍1 K-摇臂赌博机2 ϵ \epsilon ϵ-贪心算法3 softmax算法4 Python实现与分析 0 专栏介绍 本专栏重点介绍强化学习技术的数学原理&#xff0c;并且采用Pytorch框架对常见的强化学习算法、案例进行实现&#xff0c;帮助读者理解并快速上手开发。同时&#xff0c;…

Java设计模式之行为型-观察者模式(UML类图+案例分析)

目录 一、基本概念 二、UML类图 三、角色设计 四、代码实现 案例一 案例二 案例三 五、总结 一、基本概念 观察者先订阅被观察者对象&#xff0c;当被观察者的状态发生变化时&#xff0c;观察者可以及时收到消息&#xff0c;在这种模式当中&#xff0c;被观察者维护了…

回归预测 | MATLAB实现SSA-DBN麻雀算法优化深度置信网络的数据多输入单输出回归预测

回归预测 | MATLAB实现SSA-DBN麻雀算法优化深度置信网络的数据多输入单输出回归预测 目录 回归预测 | MATLAB实现SSA-DBN麻雀算法优化深度置信网络的数据多输入单输出回归预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 基于麻雀算法优化深度置信网络(SSA-DB…

【Java】顺序表ArrayList

文章目录 一、顺序表二、ArrayList 的简介三、ArrayList 的使用3.1 构造方法3.2 常见操作3.3 遍历方法3.4 扩容机制 四、ArrayList 的模拟实现五、ArrayList 的使用案例5.1 扑克牌案例5.2 杨辉三角案例 六、ArrayList 存在的问题 一、顺序表 顺序表&#xff08;Sequential Lis…

JS中的字典和散列表

前言 除了集合&#xff0c;我们还可以用字典和散列表来存储唯一值。 集合学习请见&#xff1a; 自定义集合和ES6集合http://t.csdn.cn/RcznA 在集合中&#xff0c;我们关注的是每个值本身。并将它作为主要元素。 而字典和散列表都是以[键:值]的形式来存储数据。 不同的…

Linux中vim的预备代码(prepare-code)设置

1、进入以下目录&#xff1a; /home/yys/.vim/plugged/prepare-code/snippet注意&#xff1a;yys是我个人的账号名称&#xff0c;每个人的都不一样&#xff01; 2、修改相应的预备代码&#xff0c;比如snippet.c 修改完之后保存&#xff0c;之后再创建c文件则会自动初始化有…

Python如何免费获取付费文档的数据, 保存word文档

目录标题 前言开发环境:模块使用:代码实现步骤:代码展示尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 开发环境: python 3.8 pycharm 模块使用: requests --> pip install requests re json time base64 docx --> pip install python-docx 第三方模…

【C++初阶】string类常见题目详解(二) —— 把字符串转换成整数、反转字符串、反转字符串 II、反转字符串中的单词 III、字符串相乘

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C初阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;C初阶】s…