0
点赞
收藏
分享

微信扫一扫

C++ 11_右值引用和移动语义

古月无语 2022-04-29 阅读 101
c++

1.右值和左值的概念

在C++11中 引入了右值引用和移动语义,可以避免一些复制的情况,提高了程序的性能

左值:表达式后依然能找到的持久对象,而右值是表达式后就会消失的对象,长见的左值有具名变量,函数或成员函数的名字,一些返回左值的引用表达式类似与++i,而右值又分为将亡右值和纯右值,纯右值可以是一些很长见的数字比如 42,x++等,而将亡右值 是在过程中会产生一个临时变量,但又不用左值去引用他,比如函数返回的非引用变量,C++中左右的值类型,都将是左值,将亡右值,纯右值之一,而区分左值和右值的区别就是看是否能够对值进行取地址,能取地址的就是左值

2. && 右值引用特性

2.1 右值引用是对一个右值进行引用,以此来延长该右值的生命周期,无论是左值还是右值都必须在初始化的时候引用它,因为引用类型并不具有绑定内存的操作,只是类型的别名

2.2 引用折叠

A: 左值和右值都是独立于它们的类型的,右值引用类型可以是左值也可以是右值
B:* 所有的右值引用叠加到右值引用还是右值引用 && &&, 其他的引用都会折叠为左值引用 当T&&为模板参数时输入左值为左值引用,输入右值就是右值应用 具体的折叠如下*
&& && ------> &&
& && --------> &
& &----------->&
&& &---------> &
C 编译器会将已经命名的右值引用视为左值,而未命名的变量设置为右值

3.右值引用优化性能,避免深拷贝

#inlude<iostream>
#include<cstdio>
#include<cstdlib>
#include<string.h>
using namespace std;

class MyString
{
public:
	MyString()
	{
		m_data = null;
		m_len =0;
	}
	~MyString()
	{
		delete m_data;
		m_data = nullptr;
	}
	MyString(const char*p)
	{
		m_len = strlen(p);
		copy_data(p);
	}
	MyString(const MyString& str) //拷贝构造
	{
		m_len = str.m_len();
		copy_data(str.m_data);
	}
	MyString& operator = (const &Mystring&str)
	{
		if(this != str)
		{
			m_len = str->len;
			copy_data(str.m_data);
		}
		return *this;
	}
//实现右值版本的,不需要内存拷贝操作
MyString(Mystring && str)
{
	m_len = str.m_len;
	m_data = str_m_data;
	str.m_len = 0;
	str.m_data = nullptr;
}
MyString& operator =(MyString&&rhs)
{
	if(this != str)
		{
			m_len = str->len;
			m_data= str_m_data;
			m_len =0;
			m_data = nullptr;		}
}

private:
	char*m_data;
	size_t m_len;
	void copy_data(const char*r)
	{
		m_data = new char[m_len+1];
		memcpy(m_data, r, m_len);
		m_data[m_len] = '\0';
	}
}

int main()
{
		MyString a; //默认构造函数构造一个对象
		a = Mystring("Hello");//调用拷贝赋值
	
}

4. std::move() –

*在使用右值的时候,可以节省拷贝的资源,std::move将左值转换成右值,从而方便应用移动语义,move将对象的状态从从一个对象转移到另外一个对象,只是转义 没有内存拷贝 *

4.1 通过std::move()可以将一个左值修改成一个右值

//接上面的Mystring
int main()
{
	Mystring a;
	a = Mystring("hello"); //调用右值赋值
	MyString b = a; //拷贝构造
	MyString c = std::move(b);//move 将左值转换成右值
}

5. std::forward() 完美转发 –

std::forward() 完美转发 实现了在参数传递过程中,保持其属性的功能,如果参数是左值,那么参数就将按照左值的传递下去,如果是右值就按照右值传递下去,在调用std::forward的时候涉及的模板类型推倒或者auto类型推导,如果我们指定类型调用std::forward的话 那么传递的就是T&& (T为我们指定的类型)

#include<iostream>
#include<utility>
using namespace std;
template<class T>
void print(T& t)
{
	cout << "L " << t <<endl;
}
template<class T>
void print(T&& t) //万能引用,既可以传递左值也可以传递右值,但是我们右个左值版本的重载,所以左值会优先调用左值版本的
{
cout << "R   " <<t <<endl;
}

template<class T>
void foo(T&&t)
{
	print(t);
	print(std::move(t));
	print(std::forward(t));
}

int main()
{
	foo(1);
	int x =10;
	int y =20;
	foo(x);
	foo(std::forward<int>(y)); //指定类型的std::forward返回int &&
}
//输出结果
L 1
R 1
R 1
-- func(x)
L 10
R 10
L 10
-- func(std::forward(y))
L 20
R 20
R 20

** 使用std::forward的时候一定要搭配着模板函数或者模板类来使用,因为这两者都会进行一个类型的推导,从而也会适应我们上面说的引用折叠,如果我们指定类型使用std::forward类似于这样的用法的时候,就是将T类型固定了,而std::forward得返回值本省也是T&& 固定了就相当于返回值为int&& 如果此时参数列表是左值,那么将会改成右值**

举报

相关推荐

0 条评论