C++--移动构造函数/移动赋值运算符

打印 上一主题 下一主题

主题 840|帖子 840|积分 2520

C++--移动构造函数/移动赋值运算符

什么是移动语义?

在C++11中,移动语义是一个重要的新特性,它可以使程序在内存管理方面更加高效,同时也提高了程序的性能
它允许将一个对象的所有权从一个对象转移到另一个对象,而不必要进行数据的拷贝。
通俗明白

我有一份材料,A同学找我借,那我把材料复印一份,把复印件给他,这叫做数据拷贝;而我如果把材料的所有权转让给他,那么他如今直接拥有原始的那份材料,这叫做移动语义。
为什么必要移动语义?

拷贝

要明白为什么我们必要移动语义,那我们就必要明白拷贝的操作
C++中有拷贝构造函数和拷贝复制运算符。拷贝,顾名思义就是重新申请一块新的内存空间,然后将必要的数据复制一份放到里面。
如果要复制的对象中涉及到了其他对象大概是指针数据的话,那么拷贝操作就是一项耗时的过程。
我们通过一个例子来演示一下是如果进行拷贝的:
定义一个简朴的类:
  1. class Myclass
  2. {
  3.     public:
  4.     Myclass(const std::string& s):str(s){};
  5.     private:
  6.     std::string str;
  7. };
复制代码
当新建一个对象A时,通报一个参数”hello,world”,A中的成员变量会存储该字符串,也就是会申请一个新的内存空间去存储该字符串:
  1.         Myclass A("hello,world");
复制代码
当我们定义一个新的对象B,并将A赋值给B时:
  1.         Myclass B=A;
复制代码
同样,B也会申请一段空间,并将A中存储的字符串拷贝过来
必要移动语义的环境

从拷贝的操作不丢脸出,如许的操作是耗时的,那在什么环境下,拷贝操作不是必要的呢?
同样照旧延续上面的例子,这里我们定义一个容器以及一个对象tmp,然后将其装入到容器中:
  1. std::vector<Myclass> myClasses;
  2. Myclass tmp("hello");
  3. myClasses.push_back(tmp);
  4. myClasses.push_back(tmp);
复制代码
每次将对象添加到容器中都会发生一次拷贝操作。
我们如今假设tmp对象在被添加到容器中两次之后就不再必要了,那第二次添加的时间是不是可以让容器直接取tmp对象的数据呢?这就是移动语义的意义,减少不必要的数据拷贝,提高程序的性能。
假设Myclass类已经实现了移动语义,我们就可以使用std::move 让myClasses容器直接转移tmp对象的数据为己用。
  1. myClasses.push_back(tmp);
  2. myClasses.push_back(std::move(tmp));
复制代码
移动语义的实现

我们必要先了解右值引用
右值引用

我们都知道C++有一个操作叫引用,现实上默认指的是左值引用,也就是对一个左值进行引用。那右值引用就是对右值的引用
通过&& 声明,同时:

  • 右值引用只能绑定到一个右值,不能绑定到左值;
  • 右值引用可以通过std::move() 将一个左值转换成右值引用
  1. int a=0;
  2. int&& tmp=a;   //error,不能引用左值
  3. int&& tmp2=0;  //correct
复制代码
移动构造函数

照旧接着上面的例子,当向容器添加一个新元素时,如果是通过拷贝的方法,那么对应执行的就是拷贝构造函数,而如果是通过移动的方式,那对应的就是移动构造函数
下面我们就为Myclass定义移动构造函数,此中string类型本身就实现了移动构造函数,以是可以直接使用std::move :
  1. Myclass(Myclass&& rValue) noexcept
  2.     :str(std::move(rValue.str))
  3.     {}
复制代码
在移动构造函数中,我们要做的就是转移成员数据str。
我们就可以使用移动构造函数去创建新的对象而无需拷贝复制了:
  1. Myclass tmp("hello");
  2. Myclass A(std::move(tmp));
复制代码
如果我们的类成员数据必要我们自己实现数据转移的话,也很简朴,就是把数据拿过来,并将原先对象的数据清晰:
假设这个类有两个成员变量int 和 char*类型:
  1. class Myclass2
  2. {
  3. private:
  4.     int data;
  5.     char* str;
  6. public:
  7.     Myclass2()
  8.     :data(30)
  9.     {
  10.         str="mrdc";
  11.     }
  12.     ;
  13.     Myclass2(Myclass2&& rValue) noexcept
  14.     :data(std::move(rValue.data))
  15.     {
  16.         rValue.data=0;  //delete data
  17.         str=rValue.str; //transfer
  18.         rValue.str=nullptr; //delete str
  19.     }
  20.     ~Myclass2()
  21.     {
  22.         if(str!=nullptr)
  23.         {
  24.             delete str;
  25.             str=nullptr;
  26.         }
  27.     };
  28. };
复制代码
通过移动构造函数创建对象B:
  1. MyClass A{};
  2. MyClass B{ std::move(A) }
复制代码
内存中的布局:

移动赋值运算符

和移动构造函数的实现类似:
  1. // 移动赋值运算符
  2. MyClass& operator=(MyClass&& myClass) noexcept
  3. {
  4.     val = myClass.val;
  5.     myClass.val = 0;
  6.     name = myClass.name;
  7.     myClass.name = nullptr;
  8.     return *this;
  9. }
复制代码
参考

https://bbs.huaweicloud.com/blogs/375866

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

雁过留声

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表