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&& 如果此时参数列表是左值,那么将会改成右值**