【C++】超详细解释:使用右值引用改进程序的性能【第一节】

本文探讨了C++中左值与右值的区别,以及如何利用右值引用改进程序性能,包括函数返回值的处理、临时对象的生命周期、移动语义的应用以及左值转右值的std::move方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++使用右值引用改进程序的性能

左值和纯右值(是否取地址)

int main()
{
    int a= 10;//左值因为可以取地址&a
    const int b= 20;//左值...
    double dx = 23.34;//左值...
    int *ip = nullptr;//左值...&ip
    &10;//纯右值!&10
    &20;//纯右值
    
}

总结:所有的具名变量对象都是左值,而右值不具名

lambda是不具名的,但是是执行体

将亡值

程序在运行或计算过程中,产生的临时量临时对象(这个临时对象不具名),称为将亡值;内置类型产生的字面值

int main()
{
    int a = 10;
    int b = 20;
    int c = 0;
    c =a + b;//这块有tmp将亡值
    a = func();//返回时构造临时对象,是字面值,是将亡值,是纯右值
    //因此不可以 func() = a; 赋值,因为字面量不可以赋值
}

函数的返回值

int func()
    int X = 10;
    return X ;
}
int main()
{
    int a=10,b=10;
    int i=0;
    i= a+ b;
    ++i;
    i++;//右值
    &a;
    a = func(); //返回8
    return 0;
}

可以观察到这个函数的返回值,是需要一个将亡值的

问:是否每一个函数的返回值都需要将亡值?

不是的,返回值的参数是&引用

左值强转成右值

我们尽量使用C++的静态转换,因为安全

C的强转,不安全,注释解释很清楚了

class Int
{
private:
	int val;
public:
	Int(int x = 0) :val(x){}
	void PrintValue() {
		cout << "val = " << val << endl;
	}
	void SetValue(int val)
	{
		this->val = 200;
	}
};
int main()
{
	Int a(10);
	const Int b(20);
	Int&& rv = Int(30);//右值 //rv右值引用

	//Int&& ra = (Int&&)a;
	Int&& ra = static_cast<Int&&>(a);//这是C++提供的静态转换

	Int&& rb = (Int&&)b;//不安全,b是常性左值,后续可能会改变b的值
	//Int&& rb = static_cast<Int&&>(b);//不具有取常性
	ra.PrintValue();
	rb.PrintValue();
	rb.SetValue(100);//我们使用的是C的强转方式,但b是const所以就会不安全
	rb.PrintValue();
	return 0;
}

常性左值引用是万能引用

编译器编译的时候,会将常性右值引用,编译成常性左值引用

Int fun(int x)
{
	Int tmp(x);
	return tmp;
}
int main()
{
	Int a = fun(1);//此处有代码优化,自动将将亡值转换过去
	//Int& b = fun(2);//xvalue  b是左值引用(需要绑定到具名值上),而fun(2)是右值引用
    //如何理解上面代码:首先返回值是将亡值,可以理解为字面量 2 ,2在后续不可以被修改了
	const Int& c = fun(3);//常性引用接收将亡值时,会将将亡值的生存期固化
	Int&& rf = fun(4);//也会将生存期固化
    const Int& c1 = fun(30);//万能
    Int&& c2 = fun(30);
	return 0;
}

函数中的局部对象,不能以引用返回

返回值是左值引用的话,其实编译器会将函数用指针编译

局部对象已经死了

//Int * const fun (int x)
Int & func(int x)
{
    Int tmp(x);
    return tmp;//return & tmp;析构
}
int main()
{
    Int a = func(1);//随机值
    Int& b = func(2);//随机值
    const Int& c = func(3);//随机值,tmp的值会残留在空间中
    return 0;
}
//右值引用
Int && func(int x)
{
    //Int tmp(x);
    //return tmp;//左值
	return Int(x);//会调用构造函数,创建不具名对象
}
int main()
{
    Int a = func(1);//随机值
    Int& b = func(2);//err
    const Int& c = func(3);//随机值
    Int&& rf = func(4);//随机值
    return 0;
}

move语义

class testClass {
public:
	int m_a;
	int m_b;
	testClass(int a) {
		m_a = a;
		m_b = 10;
	}
	testClass(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
};
int main() {
	vector<string> arr;
	string s1 = "213";
	arr.push_back(my_move(s1));
	cout << "s1 = " << s1 << '\n';
	vector<testClass> list;
	testClass t1{ 3 };
	testClass t2{ 4,9 };
	list.push_back(t1);
	list.push_back(std::move(t2));
	for (auto& x : list)
		cout << x.m_a << " " << x.m_b << endl;
	cout << t2.m_a <<" " << t2.m_b << endl;
	return 0;
}
/*
std::move 主要用于转移资源的所有权,但对于简单类型(如基本数据类型 int)而言,并没有资源的所有权需要转移。在这里,m_a 和 m_b 是 int 类型,它们的值会被移动,但没有堆内存、文件句柄等需要特殊处理的资源。

所以,虽然 t2 的成员变量的值被移动到了 list 中,但 t2 本身并没有被清空或销毁。你仍然可以在 main 函数中访问 t2 的成员变量,因为它们仍然存在,只是在 list 中的某个对象中。
*/

在这里插入图片描述

我们知道移动语义是通过右值引用来匹配临时值(将亡值,不具名对象)的,那么,普通的左值是否也能借助移动语义来优化性能呢,那该怎么做呢?
C++11为了解决这个问题,提供了std::move方法来将左值转换为右值,从而方便应用移动语义。move是将对象中资源的所有权从一个对象转移到另一个对象,只是转移,没有堆内存的申请,拷贝和释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bright_汉堡包

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

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

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

打赏作者

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

抵扣说明:

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

余额充值