【深入理解C++】左值引用、const引用、右值引用、std::move()函数

1.左值引用

左值引用只能绑定到左值上,不能绑定到右值上。

左值引用不能绑定到临时变量上,因为临时变量被系统当作右值。

所有变量都是左值,因为它们是有地址的。

任何函数里边的形参都是左值。

#include <iostream>
using namespace std;

int main()
{
	int& ref1 = 20; // 错误,左值引用不能绑定到右值上

	int a = 10, b = 20;

	int& ref2 = a; // 正确,左值引用能绑定到左值上	

	int& ref3 = a + b; // 错误,左值引用不能绑定到右值上

	return 0;
}

返回左值引用的函数、赋值运算符、下标运算符、解引用运算符、前置递增运算符、前置递减运算符等,返回的都是左值,可以将左值引用绑定到这类表达式的结果上。

#include <iostream>
using namespace std;

int main()
{
	int i = 10;
	int& ref = ++i; // 左值引用可以绑定到左值上,ref就变成了i的别名
	i += 5;
	cout << i << endl; // 16
	cout << ref << endl; // 16

	return 0;
}

2.const引用

引用可以被 const 修饰,这样就无法通过引用修改数据了,称为常引用。

const 必须写在 & 符号的左边,才能算是常引用。

引用 int &p 相当于 int* const p,常引用 const int &p 相当于 const int* const p

#include <iostream>
using namespace std;

int main() {

	int height = 20;
	int age = 10;

	// p1不能修改指向,但是可以利用p1间接修改所指向的变量
	int* const p1 = &age;
	//p1 = &height; // 报错
	*p1 = 30;
	cout << age << endl; // 30

	// ref1不能修改指向,但是可以通过ref1间接修改所指向的变量
	int & const ref1 = age;
	ref1 = 40;
	cout << age << endl; // 40

	// p2可以修改指向,但是不可以利用p2间接修改所指向的变量
	int const* p2 = &age;
	p2 = &height;
	//*p2 = 30; // 报错

	// ref2不能修改指向,也不可以通过ref2间接修改所指向的变量
	int const &ref2 = age; // 常引用
	//ref2 = 40; // 报错

	return 0;
}

2.1 const引用可以指向临时数据

举例1:const引用指向常量

#include <iostream>
using namespace std;

int main()
{
	const int& ref = 30;

	return 0;
}

在这里插入图片描述

举例2:const引用指向表达式

#include <iostream>
using namespace std;

int main()
{
	int a = 1;
	int b = 2;
	const int& ref = a + b;

	return 0;
}

在这里插入图片描述

举例3:const引用指向函数返回值

#include <iostream>
using namespace std;

int func()
{
	return 8;
}

int main()
{
	const int& ref = func();

	return 0;
}

在这里插入图片描述

2.2 const引用可以指向不同类型的数据

当const引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量。

举例1:查看const引用指向相同类型数据的汇编代码

#include <iostream>
using namespace std;

int main()
{
	int age = 10;
	
	const int& ref = age;
	
	age = 30;

	cout << age << endl; // 30
	cout << ref << endl; // 30

	return 0;
}

在这里插入图片描述

举例2:查看const引用指向不同类型数据的汇编代码

#include <iostream>
using namespace std;

int main()
{
	int age = 10;

	const long& ref = age;

	age = 30;

	cout << age << endl; // 30
	cout << ref << endl; // 10

	return 0;
}

在这里插入图片描述

2.3 const引用作为函数参数

如果引用参数是const,则编译器在下面两种情况下生成临时变量:

  • 实参类型是正确的,但不是左值
  • 实参类型不正确,但可以转换为正确的类型

const引用作为函数参数时,可以接受const和非const实参。

非const引用作为函数参数时,只能接受非const实参。

const引用跟非const引用可以构成函数重载。

const引用作为函数参数时的上述规则也适用于const指针。

#include <iostream>
using namespace std;

int sum(int &v1, int &v2) {
	cout << "sum(int &v1, int &v2)" << endl;
	return v1 + v2;
}

int sum(const int &v1, const int &v2) {
	cout << "sum(const int &v1, const int &v2)" << endl;
	return v1 + v2;
}

int main() {

	// 非const实参
	int a = 10;
	int b = 20;
	sum(a, b);
	
	// const实参
	const int c = 10;
	const int d = 20;
	sum(c, d);
	
	sum(10, 20);

	return 0;
}

输出结果如下:

在这里插入图片描述

3.右值引用

右值引用只能绑定到右值上,不能绑定到左值上。右值引用通常绑定到一些即将销毁的或一些临时的对象上。

#include <iostream>
using namespace std;

int main()
{
	int&& ref1 = 10; // 正确,右值引用能绑定到右值上

	int a = 100, b = 200;

	int&& ref2 = a; // 错误,右值引用不能绑定到左值上

	int&& ref3 = a * b; // 正确,右值引用能绑定到右值上

	return 0;
}

返回非引用类型的函数、算术运算符、关系运算符、位运算符、后置递增运算符、后置递减运算符等,返回的都是右值,不能将左值引用绑定到这类表达式上,可以将const引用或右值引用绑定到这类表达式上。

#include <iostream>
using namespace std;

int main()
{
	int i = 10;
	int&& ref = i++; // 右值引用绑定到临时变量上,此后ref和i没有关系
	i += 5;
	cout << i << endl; // 16
	cout << ref << endl; // 10

	return 0;
}

4.std::move()函数

std::move() 的能力只有一个:把一个左值强制转换成一个右值。实际上这个函数根本就没有做移动的操作。

例子1:

#include <iostream>
using namespace std;

int main()
{
	int i = 10;
	int&& ref = std::move(i); // 把一个左值强制转换成一个右值
	i = 20;
	cout << i << endl; // 20
	cout << ref << endl; // 20

	return 0;
}

例子2:

#include <iostream>
using namespace std;

int main()
{
	int&& ref1 = 100;
	int&& ref2 = std::move(ref1); // 把一个左值强制转换成一个右值
	ref1 = 200;
	cout << ref1 << endl; // 200
	cout << ref2 << endl; // 200

	return 0;
}

例子3:

#include <iostream>
using namespace std;

int main()
{
	string src = "I love China!";
	string dst = std::move(src); // string里的移动构造函数把src的内容转移到dst中去了,而不是std::move()转移的
	cout << "&src = " << &src << ", src = " << src << endl;
	cout << "&dst = " << &dst << ", dst = " << dst << endl;

	return 0;
}

输出结果如下:

在这里插入图片描述

例子4:

#include <iostream>
using namespace std;

int main()
{
	string src = "I love China!";
	string&& ref = std::move(src); // 把一个左值强制转换成一个右值
	cout << "&src = " << &src << ", src = " << src << endl;
	cout << "&dst = " << &ref << ", dst = " << ref << endl;

	return 0;
}

输出结果如下:

在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值