左值和右值


一、前提了解

1、临时对象被创建的情况

  • 以值的形式给函数传参(值传递):
    • 参数类型是类类型时,函数调用时,这时会调用拷贝构造函数来创建一个对象,分配内存空间,利用实参的值给这个对象初始化,生成的对象可以称为实参的一个副本,那么所有在函数里的操作都是针对于这个副本的,不会影响到原参数,函数结束后,这个临时对象就会被撤销(析构函数)
class TestTemp {
public:
	int data;
	int id;
private:
	static int count;
public:
	TestTemp(int a = 0);
	TestTemp(TestTemp& t) {
		data = t.data;
		id = count++;
		cout << "拷贝构造函数" << "data= " << data << " id= " << id<< endl << endl;;
	}
	virtual ~TestTemp() {
		cout << "析构函数" << "id= "<<id<<endl << endl;;
	}
	TestTemp& operator=(const TestTemp& t) {
		cout << "赋值运算符重载" << endl << endl;;
		data = t.data;
		return *this;
	}
	int getObj(TestTemp t);
};
int TestTemp::count = 0;
TestTemp::TestTemp(int a){
	this->data = a;
	this->id = count++;
	cout << "构造函数: " << "data= " << data << " id= " << id << endl<<endl;
}
int TestTemp::getObj(TestTemp t) {
	int temp = t.data;
	t.data = 10000;
	return temp;
}
void test_06() {
	TestTemp tt(10);
	cout << "值传递产生临时对象测试: " << endl<<endl;
	cout << "tt.getObji = " << tt.getObj(tt) << endl;
	cout << "tt.data= " << tt.data << endl << endl;
}

1.tt调用构造函数
2.getObj的参数 t 调用拷贝构造函数,相当于 TestTemp t = tt;给t分配内存空间并用tt 初始化
3. t 的地址和 tt 是不一样 ,只是一个临时对象,对 tt不会造成影响;函数完毕,就会调用析构函数并释放内存
在这里插入图片描述

  • 类型转换:
    • 一般是会先定义一个对象,然后给这个对象赋值;
    • 此时如果赋值的对象类型不正确,就会把这个对象当作参数传给构造函数的形参(但类型要和构造函数参数类型匹配),构造函数就会生成一个临时对象,把这个临时对象赋值给tt1,结束后就会调用析构函数
void test_06() {
	cout << "类型转换产生临时对象测试: " << endl << endl;
	TestTemp tt1;
	tt1 = 20;				
	//TestTemp tt1 = 30;
	//这种形式,就会直接构造,而不是赋值	
}

1.tt1 调用构造函数
2. 20 类型不正确,调用构造函数,把20作为参数传进去,生成一个临时对象
3.类型正确后,调用拷贝构造函数给 tt1 赋值 ,复制完毕,临时对象就会析构释放内存
在这里插入图片描述

  • 函数返回一个对象: 函数需要返回一个对象时,函数会在栈中创建一个临时对象又叫匿名对象(类对象还会调用拷贝构造函数)来存储函数的返回值
//类中增加一个函数
friend TestTemp fun(TestTemp& t);
//类外定义该函数
TestTemp fun(TestTemp& t) {
	cout << "fun: " << endl;
	t.data = t.data * 4;
	cout << "fun t.data change" << endl;
	return t;
}
void test_06() {
	cout << "函数返回产生临时对象测试: " << endl << endl;
	TestTemp tt2(40);
	TestTemp tt3;
	tt3 = fun(tt2);
	cout<<"tt2.data= "<<tt2.data<<endl;
}

1.tt2调用构造函数,tt3调用构造函数
2.fun函数返回的是TestTemp类型的对象,相当于 TestTemp 临时 = t,此时调用拷贝构造函数(t是一个对象)
3.调用赋值运算符函数给 tt3 赋值 ,完毕后,临时对象析构释放内存
在这里插入图片描述


2.拷贝构造函数的参数必须是该类的引用类型

如果拷贝构造函数的参数不是一个该类的引用类型,而是诸如const T 或 T 这样的类型,那么在我们调用拷贝构造函数时,会采用传值的方式将实参的值传给形参的值,这种方式又会调用拷贝构造函数,从而造成无穷的递归调用拷贝构造函数


3.复制对象和移动对象

  • 复制对象: 将B的内容复制给A
    • 先创建对象A 即开辟空间
    • 复制B的内容
    • 上述可能会用到拷贝构造函数和赋值运算符函数
  • 移动对象:
    • 一个对象B被创建了有了内存空间,但B并不具有这片空间的使用权,B会将内容直接交给A来接管即移动到A【交接完成,可以把B指向内存的指针置空】
    • 上述的B可以理解为 将被销毁/临时 对象,是右值

二、左值和右值

1.右值

右值:不能用取地址 & 运算符获得对象的内存地址,表达式结束后就不再存在的临时对象;位于等号右边

可以分为将亡值和纯右值:
将亡值xvalue:右值引用相关的表达式类型,该表达式是要被移动的对象

  • 右值引用: 对一个右值的引用 (别名/绑定),一因为右值本身不具有名字,那么只能通过引用来关联它们
  • Type &&引用名 = 右值表达式
  • 右值引用的用途: 完成对一个将亡值的语义转移过程,使我们在复制具有大块内存空间时,可以直接使用原对象已经分配好的内存空间,进而省去重新分配内存空间的过程

纯右值: 临时值——函数返回的临时变量、字面量值、lambda表达式

为什么右值不能被取地址:

  • 对于临时对象,它可以存储于寄存器,所以没办法用 取地址 运算符
  • 对于常量,它可能被编码到机器指令的“立即数”中,所以没办法用 取地址 运算符

std::move:

  • 并不能移动任何东西,唯一的功能将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义
  • 实现上,std::move 等同于 强制类型转换 static_cast<T&&> (leftValue)
  • 可以避免大量内存分配的高昂代价

2.左值

左值: 能用取地址&运算符获得对象的内存地址,表达式结束后该对象依然存在

  • 左值可以出现在等号左边或右边
  • 所有的具名变量或对象都是左值
  • 左值引用: 给左值起别名,Type& 引用名 = 左值表达式,

3.总结

左值看地址,右值看内容
拷贝构造还是构造,赋值针对于一个已有的对象

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明前大奏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值