移动构造

  • 移动构造函数写法如下,&&表示实现右值引用

    1. class_name ( class_name && )

    在现实中有很多这样的例子,我们将钱从一个账号转移到另一个账号,将手机SIM卡转移到另一台手机,将文件从一个位置剪切到另一个位置……移动构造可以减少不必要的复制,带来性能上的提升。

  • C++11标准中提供了一种新的构造方法——移动构造。

  • C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。在某些情况下,我们没有必要复制对象——只需要移动它们。
  • C++11引入移动语义:
    • 源对象资源的控制权全部交给目标对象
  • 移动构造函数

    问题与解决

  • 当临时对象在被复制后,就不再被利用了。我们完全可以把临时对象的资源直接移动,这样就避免了多余的复制操作。

  • *什么时候该触发移动构造?有可被利用的临时对象,大白话就是大多时候是深copy,有时候会用浅copy,同时兼顾这种情况就用移动copy,避免偷资源。
  • 传入的值不管是左值还是右值(包括用std::move得到的将亡值),都强制转成左值。

image.png

实例代码

  1. #include<iostream>
  2. using namespace std;
  3. class IntNum {
  4. public:
  5. IntNum(int x = 0) : xptr(new int(x)){ //构造函数
  6. cout << "Calling constructor..." << endl;
  7. }
  8. IntNum(IntNum && n): xptr( n.xptr){ //移动构造函数,&&实现n的右值引用
  9. n.xptr = nullptr;
  10. cout << "Calling move constructor..." << endl;
  11. }
  12. ~IntNum(){ //析构函数
  13. delete xptr;
  14. cout << "Destructing..." << endl;
  15. }
  16. int getInt() { return *xptr; }
  17. private:
  18. int *xptr;
  19. };
  20. //返回值为IntNum类对象
  21. IntNum getNum() {
  22. IntNum a;
  23. return a;
  24. }
  25. int main() {
  26. cout<<getNum().getInt()<<endl;
  27. return 0;
  28. }

右值引用

要弄明白右值引用到底是怎么一回事,必须要对左值和右值做一个明确的理解。
左值(lvalue, left value),顾名思义就是赋值符号左边的值。准确来说, 左值是表达式(不一定是赋值表达式)后依然存在的持久对象。
右值(rvalue, right value),右边的值,是指表达式结束后就不再存在的临时对象。
而 C++11 中为了引入强大的右值引用,将右值的概念进行了进一步的划分,分为:纯右值、将亡值。
纯右值(prvalue, pure rvalue),纯粹的右值,要么是纯粹的字面量,例如 10, true; 要么是求值结果相当于字面量或匿名临时对象,例如 1+2。非引用返回的临时变量、运算表达式产生的临时变量、 原始字面量、Lambda 表达式都属于纯右值。
要拿到一个将亡值,就需要用到右值引用:T &&,其中 T 是类型。 右值引用的声明让这个临时值的生命周期得以延长、只要变量还活着,那么将亡值将继续存活。
C++11 提供了 std::move 这个方法将左值参数无条件的转换为右值, 有了它我们就能够方便的获得一个右值临时对象,例如:

  1. #include <iostream>
  2. #include <string>
  3. void reference(std::string& str) {
  4. std::cout << "左值" << std::endl;
  5. }
  6. void reference(std::string&& str) {
  7. std::cout << "右值" << std::endl;
  8. }
  9. int main()
  10. {
  11. std::string lv1 = "string,"; // lv1 是一个左值
  12. // std::string&& r1 = lv1; // 非法, 右值引用不能引用左值
  13. std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值
  14. std::cout << rv1 << std::endl; // string,
  15. const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的生命周期
  16. // lv2 += "Test"; // 非法, 常量引用无法被修改
  17. std::cout << lv2 << std::endl; // string,string,
  18. std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象生命周期
  19. rv2 += "Test"; // 合法, 非常量引用能够修改临时变量
  20. std::cout << rv2 << std::endl; // string,string,string,Test
  21. reference(rv2); // 输出左值
  22. return 0;
  23. }

参考:https://changkun.de/modern-cpp/zh-cn/03-runtime/#%E5%B7%A6%E5%80%BC%E3%80%81%E5%8F%B3%E5%80%BC%E7%9A%84%E7%BA%AF%E5%8F%B3%E5%80%BC%E3%80%81%E5%B0%86%E4%BA%A1%E5%80%BC%E3%80%81%E5%8F%B3%E5%80%BChttps://www.bilibili.com/read/cv15303835?spm_id_from=333.999.0.0
https://www.bilibili.com/video/BV1Di4y1o7CD?from=search&seid=5179503673960740822&spm_id_from=333.337.0.0