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


    1.左值引用

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

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

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

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

    #include 
    using namespace std;
    
    int main()
    {
    	int& ref1 = 20; // 错误,左值引用不能绑定到右值上
    
    	int a = 10, b = 20;
    
    	int& ref2 = a; // 正确,左值引用能绑定到左值上	
    
    	int& ref3 = a + b; // 错误,左值引用不能绑定到右值上
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.const引用

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

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

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

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

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

    举例1:const引用指向常量

    #include 
    using namespace std;
    
    int main()
    {
    	const int& ref = 30;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

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

    #include 
    using namespace std;
    
    int main()
    {
    	int a = 1;
    	int b = 2;
    	const int& ref = a + b;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

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

    #include 
    using namespace std;
    
    int func()
    {
    	return 8;
    }
    
    int main()
    {
    	const int& ref = func();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

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

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

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

    #include 
    using namespace std;
    
    int main()
    {
    	int age = 10;
    	
    	const int& ref = age;
    	
    	age = 30;
    
    	cout << age << endl; // 30
    	cout << ref << endl; // 30
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

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

    #include 
    using namespace std;
    
    int main()
    {
    	int age = 10;
    
    	const long& ref = age;
    
    	age = 30;
    
    	cout << age << endl; // 30
    	cout << ref << endl; // 10
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    2.3 const引用作为函数参数

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

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

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

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

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

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

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出结果如下:

    在这里插入图片描述

    3.右值引用

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

    #include 
    using namespace std;
    
    int main()
    {
    	int&& ref1 = 10; // 正确,右值引用能绑定到右值上
    
    	int a = 100, b = 200;
    
    	int&& ref2 = a; // 错误,右值引用不能绑定到左值上
    
    	int&& ref3 = a * b; // 正确,右值引用能绑定到右值上
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.std::move()函数

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

    例子1:

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    例子2:

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    例子3:

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出结果如下:

    在这里插入图片描述

    例子4:

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出结果如下:

    在这里插入图片描述

  • 相关阅读:
    FestDFS
    js验证字符串是否是时间日期格式
    从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十一)spring-boot-admin 监控篇(1) 原理与介绍
    【JAVASE】Java泛型实例化
    c++常用函数所在头文件一览
    JSP 社区联动系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
    来自多彩世界的控制台——C#控制台输出彩色字符画
    Android 11.0 默认授予app获取序列号SerialNo权限
    vue设计原理-带你重走vue诞生路程
    Spring的BeanDefinition的作用和使用方法
  • 原文地址:https://blog.csdn.net/qq_42815188/article/details/89100126