【C++】右值引用

简介: 【C++】右值引用

右值引用

能够取地址、能够被修改的被称之为左值。

不能够取地址、不能够被修改、以及将亡值被称之为右值。

  1. 普通类型的变量,因为有名字,可以取地址,都认为是左值。
  2. const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是 const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间), C++11认为其是左值。
  3. 如果表达式的运行结果是一个临时变量或者对象,认为是右值。
  4. 如果表达式运行结果或单个变量是一个引用则认为是左值。

C++11对右值进行了严格的区分: C语言中的纯右值,比如:a+b, 100 将亡值。比如:表达式的中间结果、函数按照值的方式进行返回。

#include<iostream>
using namespace std;
void test(int a=0)
{
    return a;//a是将亡值
}
int main(void)
{
    int a=10;//a是左值,10是右值
    int b=20;//b是左值,20是右值
    b=a;//左值a也可以放在等于号=右边
    return 0;
}

值得形式返回对象的缺陷

#include<iostream>
#include<string>
using namespace std;
class String
{
public:
  String(const char* str = "")
  {
    cout << "构造函数()->String(const char* str = "")" << endl;
    if (nullptr == str)
    str = "";
    _str = new char[strlen(str) + 1];
    strcpy(_str, str);
  }
  String(const String & s)
  : _str(new char[strlen(s._str) + 1])
  {
    strcpy(_str, s._str);
  }
  String& operator=(const String& s)
  {
     if (this != &s)
     {
       char* pTemp = new char[strlen(s._str) + 1];
       strcpy(pTemp, s._str);
       delete[] _str;
       _str = pTemp;
     }  
    return *this;
  }
 String operator+(const String& s)
 {
   char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];
   strcpy(pTemp, _str);
   strcpy(pTemp + strlen(_str), s._str);
   String strRet(pTemp);
   return strRet;
 }
 ~String()
 {
   if (_str) delete[] _str;
 }
private:
  char* _str;
};
int main()
{
  String s1("hello");
  String s2("world");
  String s3=s1 + s2;
  return 0;
}

对于如上代码的operator+中,返回strRet的时候需要临时创建一个对象,然后销毁strRet,最后再把strRets3

这乍一看是没什么问题,但是这里的strRet临时对象s3的内容是完全相同的,但是开辟了三块空间,是不是太浪费空间了。

移动语句

C++11当中,如果需要实现移语句,必须使用右值引用。

#include<iostream>
using namespace std;
class String
{
public:
  String(const char* str = "")
  {
    cout << "构造函数()->String(const char* str = "")" << endl;
    if (nullptr == str)
    str = "";
    _str = new char[strlen(str) + 1];
    strcpy(_str, str);
  }
  String(const String & s)
    : _str(new char[strlen(s._str) + 1])
  {
    strcpy(_str, s._str);
  }
  void swap(String& s)
  {
    std::swap(_str,s._str);
  }
  String(String&& s)noexcept
    :_str(nullptr)
  {
    swap(s);
  }
  String& operator=(const String& s)
  {
     if (this != &s)
     {
       char* pTemp = new char[strlen(s._str) + 1];
       strcpy(pTemp, s._str);
       delete[] _str;
       _str = pTemp;
     }  
    return *this;
  }
 String operator+(const String& s)
 {
   char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];
   strcpy(pTemp, _str);
   strcpy(pTemp + strlen(_str), s._str);
   String strRet(pTemp);
   return strRet;
 }
 ~String()
 {
   if (_str) delete[] _str;
 }
private:
  char* _str;
};
int main()
{
  String s1("hello");
  String s2("world");
  String s3=(s1 + s2);
  return 0;
}

代码添加一个移动构造,就可以减少申请空间,在strRet返回的时候会去调用String(String&& s)noexcept,调用String(String&& s)noexcept的时候可以看到,直接把sthis中的资源做了交换,出了作用域,s就会被销毁,那么在销毁之前把资源先给this不是就可以减少拷贝了吗?

移动赋值

移动赋值和移动构造一样,可以减少拷贝,将现有的资源最大利用,减少空间的开辟。

#include<iostream>
using namespace std;
class String
{
public:
  String(const char* str = "")
  {
    cout << "构造函数()->String(const char* str = "")" << endl;
    if (nullptr == str)
    str = "";
    _str = new char[strlen(str) + 1];
    strcpy(_str, str);
  }
  String(const String & s)
    : _str(new char[strlen(s._str) + 1])
  {
    strcpy(_str, s._str);
  }
  void swap(String& s)
  {
    std::swap(_str,s._str);
  }
  //移动构造
  String(String&& s)noexcept
    :_str(nullptr)
  {
    cout << "移动构造(资源移动)" << endl;
    swap(s);
  }
  // 移动赋值
  String& operator=(String&& s)noexcept
  {
    cout << "String& operator=(string s) -- 移动赋值(资源移动)" << endl;
    swap(s);
    return *this;
  }
  String& operator=(const String& s)
  {
     if (this != &s)
     {
       char* pTemp = new char[strlen(s._str) + 1];
       strcpy(pTemp, s._str);
       delete[] _str;
       _str = pTemp;
     }  
    return *this;
  }
 String operator+(const String& s)
 {
   char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];
   strcpy(pTemp, _str);
   strcpy(pTemp + strlen(_str), s._str);
   String strRet(pTemp);
   return strRet;
 }
 ~String()
 {
   if (_str) delete[] _str;
 }
private:
  char* _str;
};
int main()
{
  String s1("hello");
  String s2("world");
  String s3=(s1 + s2);
  s3 = move(s1);//强行转换成将亡值
  return 0;
}

这里的move就是强制类型转化成一个将亡值,让其变成右值,好去调用我们这里的移动赋值(这里只是为了强行调用移动赋值,这样的代码其实是有问题的,会导致s1变成nullptr)。

move(s1)==static_cast<String&&>(s1);

目录
相关文章
|
6月前
|
算法 编译器 程序员
【C/C++ 解惑 】 std::move 将左值转换为右值的背后发生了什么?
【C/C++ 解惑 】 std::move 将左值转换为右值的背后发生了什么?
69 0
|
6月前
|
C++
c++左值和右值,左值引用和右值引用
c++左值和右值,左值引用和右值引用
54 0
|
2月前
|
编译器 C++
C++ 11新特性之右值引用
C++ 11新特性之右值引用
45 1
|
6月前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
36 1
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
|
6月前
|
存储 安全 C语言
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(上)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
34 2
|
6月前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(下)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
38 1
|
5月前
|
编译器 C++ 开发者
C++一分钟之-右值引用与完美转发
【6月更文挑战第25天】C++11引入的右值引用和完美转发增强了资源管理和模板灵活性。右值引用(`&&`)用于绑定临时对象,支持移动语义,减少拷贝。移动构造和赋值允许有效“窃取”资源。完美转发通过`std::forward`保持参数原样传递,适用于通用模板。常见问题包括误解右值引用只能绑定临时对象,误用`std::forward`,忽视`noexcept`和过度使用`std::move`。高效技巧涉及利用右值引用优化容器操作,使用完美转发构造函数和创建通用工厂函数。掌握这些特性能提升代码效率和泛型编程能力。
47 0
|
6月前
|
编译器 C++ 容器
【C++11(一)】右值引用以及列表初始化
【C++11(一)】右值引用以及列表初始化
|
6月前
|
存储 安全 程序员
C++11:右值引用
C++11:右值引用
45 0
|
6月前
|
存储 算法 程序员
【C++入门到精通】右值引用 | 完美转发 C++11 [ C++入门 ]
【C++入门到精通】右值引用 | 完美转发 C++11 [ C++入门 ]
54 0