前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >理解 C++ 右值引用和 std::move

理解 C++ 右值引用和 std::move

原创
作者头像
Rock_Lee
修改2021-08-27 15:11:33
8110
修改2021-08-27 15:11:33
举报
文章被收录于专栏:知识碎片知识碎片

为理解这两个概念需要先了解以下内容:

  1. 左值,右值
  2. 拷贝构造函数和复制构造函数

左值和右值

一般来说,左值代表某处内存区域,相对的,右值只代表值

代码语言:txt
复制
#include <iostream>
#include <string>
#include <utility>
#include <vector>
class A
{
public:
    A(){}
    ~A(){}

    void init(int &val) //val is l-ref
    {
        std::cout<<"use & print"<<std::endl;
    }
    void init(int &&val) //val is r-ref
    {
        std::cout<<"use && print"<<std::endl;
    }
};

/*
**  左值 右值
**  int tmp = 10;
**  tmp 是一个左值,左值一般是变量,可以被引用,10是一个右值,不可以被引用.
**  一般来说,左值代表某处内存区域,相对的,右值只代表值
*/
void func_l_rvalue()
{
    int tmp =10;
    A().init(tmp);              //call &
    A().init(10);               //call &&
}

/*
** && 右值引用 和 std::move
**    右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,
** 这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。
** C+11之前通过拷贝构造函数和拷贝赋值操作符为类设计了拷贝/复制,没有实现对资源移动操作。
**    std::move 可以理解为把一个左值临时性地 cast 成右值 
*/
void func_rvalue_ref()
{
    int tmp =10;
    A().init(std::move(tmp));   //call &&
}

/*
** 复制和移动语义
**
*/
std::vector<std::string> test_str_split(const std::string& s)
{
    std::vector<std::string> v;
    return v;
}

void call_test_str_split()
{
    //c++11之前,没有移动语义
    std::vector<std::string> v1 = test_str_split("1,2,3"); //返回的值vector用以拷贝构造对象v1,为v1申请堆内存,复制数据,然后析构临时对象,释放堆内存。
    std::vector<std::string> v2;    // 返回的vector被复制给对象v2(拷贝复制运算符),需要先清理v2原数据,将临时对象数据复制给v2,然后析构临时对象,
    v2 = test_str_split("1,2,3");

    //同样的代码,在具备移动语义之后的C++11里
    std::vector<std::string> v1 = test_str_split("1,2,3"); //返回的vector用以移动构造对象,v1直接取走临时对象的堆上内存,无需新申请,之后临时对象编程空客,不再拥有任何资源,析构也不用释放内存。
    std::vector<std::string> v2;    // 返回的vector被移动给对象v2,移动赋值运算符,先释放v2原有数据,然后直接从返回值取走数据,然后返回值被析构。
    v2 = test_str_split("1,2,3");
}
int main()
{
    func_l_rvalue();
    func_rvalue_ref();
    return 0;
}
```c++

上述涉及到的移动语义,是由C++11之前存在的一些历史遗留问题,使C++标准库的实现在多种场景下消除了不必要的额外开销(如std::vector, std::string).这些问题都由于构造函数和拷贝构造函数以及赋值构造函数引起.

拷贝构造函数和赋值构造函数

代码语言:txt
复制
#include <iostream>
#include <string>
using namespace std;

class Student{
public:
    Student(string name = "", int age = 0, float score = 0.0f); 
    Student(const Student& stu);
    Student& operator=(const Student& stu);     // return a reference for calling assignment operator above twice: a = b = c,
public:
    void display();
private:
    string m_name;
    int m_age;
    float m_score;
};

Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){ }

Student::Student(const Student &stu){
    this->m_name = stu.m_name;
    this->m_age = stu.m_age;
    this->m_score = stu.m_score;
    cout<<"Copy constructor was called."<<endl;
}

Student& Student::operator=(const Student &stu){
    if(this == &stu) return *this;
    this->m_name = stu.m_name;
    this->m_age = stu.m_age;
    this->m_score = stu.m_score;
    cout<<"Assignment operator constructor was called."<<endl;
    return *this;
}


/* 3种调用拷贝构造函数的场景
** 1) 一个对象以值传递的方式传入函数体
** 2)一个对象以值传递的方式从函数返回
** 3)一个对象需要通过另一个对象进行初始化
*/ 
int main(){
    Student stu1("James", 16, 90.5);
    Student stu2 = stu1;        // calls copy ctor 强调:此处stu2对象不存在,因此用stu1对象来构造和初始化stu2, 调用拷贝构造函数。
    Student stu3(stu1);         // calls copy ctor
    Student stu4;
    stu4 = stu1;                // calls copy assignment operator
    return 0;
}

C++通过拷贝构造函数和拷贝赋值操作符为类设计了拷贝/复制的概念,但为了实现对资源的移动操作,调用者必须使用先复制、再析构的方式。否则,就需要自己实现移动资源的接口。

回到原题

为什么需要右值引用?

右值引用其实就为给匿名(天生匿名或者通过 std::move 将名字失效,这样的对象即将被析构)对象重新起名字。 我们一直所说的将亡值其实就是所谓的右值,我们可以利用右值引用将将亡值利用起来,减少不必要的构造和析构。

std::move就因而产生.

std::move的常用姿势

接管资源

代码语言:txt
复制
void My::take(Book && iBook) 
{
  mBook = std::move(iBook); //将没人要的iBook,拿过来据为己有

}

转移所有权

代码语言:txt
复制
auto thread = std::thread([]{});
std::vector<std::thread> lThreadPool;
lThreadPool.push\_back(std::move(thread)); //现在thread pool来掌控着thread

避免拷贝

代码语言:txt
复制
void f() {
  std::vector v = ...;
  take(std::move(v)); // 直接move进了函数g里面,不用拷贝
}
代码语言:txt
复制

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 左值和右值
  • 拷贝构造函数和赋值构造函数
  • 回到原题
    • 接管资源
      • 转移所有权
        • 避免拷贝
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档