C++左值引用与右值引用

C++左值引用与右值引用_第1张图片

0.类型和值类别的区别?

类型(type)和值类别(value category)

1.类型指的是数据类型,int,char这样的内置类型,类型主要是用来区别它们的字节大小。除了内置类型还有自定义类型,自定义类型中的类型还表征了结构,像C语言的结构体,由于结构(或者说内置类型的顺序)的不同引发的内存对齐问题。所以类型表征的是大小,结构。

2.值类别呢,就是关于变量的左右值属性

1.如何区分左值右值?

1.从高级语言的角度--能否被引用

如果能取地址,说明这个变量是左值,我们可以通过地址修改它,如果不能取地址,则变量是右值,我们不能通过地址修改它。

比如int num = 10;

将10存储到变量num中,num对应一个地址,后续可以通过地址修改num的值,所以我们称num为左值(num在表达式的左边,这是左的含义),表达式右边的10就是一个右值,我们无法通过10的地址修改10。

2.从高级语言的角度--值类别表征了数据的存储位置

从计算机体系结构的角度理解

int num = 10;

全新的理解是:num不是什么变量名,num对应了一个地址,是位于进程地址空间上的栈区的地址,int也不是什么类型,int表示从该地址往后8字节的空间被进程使用了,要以8字节为一个整体,修改该地址上的内容。

而10是一个字面常量,用二进制表示为00001010,根据赋值对象的不同再进行提升或截断,比如10要赋值给int对象,所以被提升为00000000 ... 00001010(前面多出7个全0的序列,每个序列有8个0),存储时再根据大小端字节序将这些字面值从代码区拷贝到刚才的栈区地址上。

这行代码被编译后会被放到进程地址空间的正文代码区,系统怎么知道你要用10初始化num?因为正文代码区的存储了10的二进制序列,代码区中还有10要放入的地址(没有什么num,只有一串地址),以及把10放到地址上的指令,这些信息都会在代码中表示。并且,程序的正文代码区也是有地址的,代码区存储系统要执行的指令。现在回头看右值的概念,不能取地址的就是右值,10有地址吗?当然有,没有地址系统怎么访问正文代码区,怎么知道你要初始化的值是10,所以不能取地址不是因为没有地址,而是因为这个地址你不能知道,地址位于只读数据区(代码区的数据可不能随便修改,当然是只读数据区)或者说该地址上的数据只有在程序运行后才会被系统读取,你要取地址,编译器直接出手,谁能保证你不会做一些危害系统安全的事,编译器可不会给你这些地址,于是程序编译失败。

总结:我认为只要数据位于的区域你没有权限访问,这些数据就是右值,你有权限访问的区域,存储的数据是左值。

2.将亡值

C++左值引用与右值引用_第2张图片

我们通常讨论的左值并不是gvalue(泛左值),而是lvalue,通常讨论的右值是rvalue,它包含了将亡值xvalue和纯右值pvalue,其中的将亡值与右值引用息息相关,匿名对象和函数返回值都是将亡值,它们都具有常属性,并且生命周期较短,在下一条语句执行前资源就会被释放。具体的比如隐式类型转换产生的中间变量,为了调用类的函数而定义的匿名对象,这些变量似乎都是工具人,被创建只是为了其他语句的成功执行

实例

左值

int x = 8;
int & func()
{
    return x;	//不能将临时变量以左值返回,这里返回个全局变量的引用
}
 
func() = 10;	//func()返回的是左值,可以放在左边被赋值
printf("x = %d", x);	//10。修改的其实是x

右值

int func()
{
    int x = 9;
    return x;
}
 
func() = 10;	//编译器报错
//你只能:
int y = func();	//func()返回的其实是临时变量,也就是右值,然后将这个右值赋给y(逻辑上可以这样理解没问题,编译器不一定按这个步骤来,请看示例二)

3.C++11右值引用

右值引用是右值吗 :右值引用不是右值,它是一个左值;

1.C++11右值分类

C++11中的右值可以分为两种:

1.纯右值:内置类型表达式的值

2.将亡值:自定义类型表达式的值:所谓的将亡值就是指声明周期马上就要结束 的值,一般来说匿名对象,临时对象,move后的自定义类型都可以看做将亡值、

2.左值引用与右值引用区别

左值引用:左值引用只能引用左值,不能引用右值。但是const左值引用既可引用左值,也可引用右值,因为 const左值引用也是只读的,而权限可以平移

int main()
{
	// 左值引用只能引用左值,不能引用右值。
	int a = 10;
	int& ra1 = a;    // ra为a的别名
	//int& ra2 = 10;   // 编译失败,因为10是右值
	
	// const左值引用既可引用左值,也可引用右值。
	const int& ra3 = 10;
	const int& ra4 = a;
	return 0;
}

右值引用只能右值,不能引用左值,但是右值引用可以move以后的左值。

int main()
{
	// 右值引用只能右值,不能引用左值。
	int&& r1 = 10;

	// error C2440: “初始化”: 无法从“int”转换为“int &&”
	// message : 无法将左值绑定到右值引用
	//int a = 10;
	//int&& r2 = a;

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

2.右值引用的作用

 右值引用是C++11新特性,之所以引入右值引用,是为了提高效率。如下面所示:

class A
{
public:
	A(size_t N):m_p(new char[N])
	{
	}
	A(const A & a)
	{
		if (this != &a)
		{
			delete[]m_p;
			m_p = new char[strlen(m_p) + 1];
			memcpy(m_p, a.m_p, strlen(m_p) + 1);
		}
	}
	~A()
	{
		delete []m_p;
	}
 
private:
	char *m_p = nullptr;
};
 
A createA(size_t N)
{
	return A(100);
}
 
void func(A a)
{
	//
}
 
int main()
{
	func(createA(100));
 
	system("pause");
	return 0;
}

在查阅过程中发现一片讲的较为透彻的文章 ,想了解更具体的可以查看C++ / 左值、右值、将亡值 | 谈谈我对它们的深入理解 - 知乎 (zhihu.com)

你可能感兴趣的:(c++,开发语言)