c++右值引用,移动语义,完美转发

c++左值和右值

左值的英文简写为“lvalue”,右值的英文简写为“rvalue”。lvalue 是“loactor value”的缩写,可意为存储在内存中、有明确存储地址(可寻址)的数据,而 rvalue 译为 “read value”,指的是那些可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)。

通常情况下,判断某个表达式是左值还是右值,最常用的有以下 2 种方法。

  1. 可位于赋值号(=)左侧的表达式就是左值;反之,只能位于赋值号右侧的表达式就是右值。
  2. 有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。

c++右值引用

C++11 标准新引入了右值引用,用 “&&” 表示。

和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化。

int num = 10;
int && a = 10;

和常量左值引用不同的是,右值引用还可以对右值进行修改。

int && a = 10;
a=100;
cout<<a;      //输出100

移动语义

vector<MyString> v_str;
v_str.push_back(MyString("hello"));

MyString(“hello”)构造出来的字符串本来就很长,构造一遍就很耗时了,最后却还要拷贝一遍,而MyString(“hello”)只是临时对象,拷贝完就没什么用了,这就造成了没有意义的资源申请和释放操作

// 移动构造函数
MyString(MyString&& str) noexcept
   :m_data(str.m_data) {
   MCtor ++;
   str.m_data = nullptr; //不再指向之前的资源了
 }
  // 移动赋值函数 =号重载
  
MyString& operator=(MyString&& str) noexcept{
   MAsgn ++;
   if (this == &str) // 避免自我赋值!!
      return *this;

   delete[] m_data;
   m_data = str.m_data;
   str.m_data = nullptr; //不再指向之前的资源了
   return *this;
 }

可以看到,移动构造函数与拷贝构造函数的区别是,拷贝构造的参数是const MyString& str,是常量左值引用,而移动构造的参数是MyString&& str,是右值引用,而MyString(“hello”)是个临时对象,是个右值,优先进入移动构造函数而不是拷贝构造函数。而移动构造函数与拷贝构造不同,它并不是重新分配一块新的空间,将要拷贝的对象复制过来,而是"偷"了过来,将自己的指针指向别人的资源,然后将别人的指针修改为nullptr

对于一个左值,肯定是调用拷贝构造函数了,但是有些左值是局部变量,生命周期也很短,也能移动而不是拷贝。C++11为了解决这个问题,提供了std::move()方法来将左值转换为右值,从而方便应用移动语义。

MyString str1("hello"); //调用构造函数
MyString str2(std::move(str1)); // 调用移动构造函数、str1内部指针已经失效

完美转发

所谓转发,就是通过一个函数将参数继续转交给另一个函数进行处理,原参数可能是右值,可能是左值,如果还能继续保持参数的原有特征,那么它就是完美的。
std::forward就可以保存参数的左值或右值特性。

template<class T,class S>
class factory{
public:
	T create(S&& arg_){
		return T (std::forward<s>(arg_));
	}
};

class LowObject{
public:
	LowObject(Resourse&& arg);
};

class HighObject{
public:
	HighObject(LowObject && arg)
}

int main(){
	factory<HighObject,LowObject> factory;
	factory.create(getLowFactory());//避免了LowObject的拷贝

}

おすすめ

転載: blog.csdn.net/weixin_53022668/article/details/121210159