• C++之移动语义


    1. 引入移动语义

    为了能够理解移动语义的目的,我们先从整成的一个类进行示范,示例如下:

    class TestClass
    {
    public:
        TestClass(int s) :m_number(s) {
            cout << "constructor!\n";
        }
        ~TestClass() {
            cout << "destructor!\n";
        }
    
        // 拷贝构造
        TestClass(const TestClass& that) :m_number(that.m_number) {
            cout << "copy constructor!\n";
        }
    
        // 赋值操作符
        TestClass& operator=(const TestClass& tc) {
            cout << "operator= is called\n";
            if (this == &tc)
                return *this;
            m_number = 0;
            m_number = tc.m_number;
            return *this;
        }
    
        int m_number;
    };
    
    
    TestClass tcFactory()
    {
        TestClass tc(10);
        return tc;
    }
    
    int main()
    {
        {
            TestClass tc = tcFactory();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    上面代码的输出结果如下:

    constructor!
    copy constructor!
    destructor!
    destructor!
    
    • 1
    • 2
    • 3
    • 4

    可以看到进行了一次构造和一次拷贝构造,拷贝构造就发生在tc 接收tcFactory()返回值时。这几产生了不必要的资源消耗,如果这里可以重用或者转移return产生的临时值(右值)是不是可以减少资源的消耗呢?C++正是使用转移的方式来处理的,这个转移就是移动构造函数,也可以说是移动语义,示例如下:

    // 其他代码不变,只增加移动构造与移动赋值处理函数
    // 移动构造
    TestClass(TestClass&& rr):m_number(rr.m_number) {
        // 如果这里是指针的变量的话则可以避免指针重复释放的问题
        rr.m_number = 0;
        cout << "move constructor!\n";
    }
    
    // 移动赋值
    TestClass& operator=(TestClass&& rr) {
        cout << "move operator= is called\n";
        if (this == &rr)
            return *this;
        // 此步骤相当于对源指针的释放
        m_number = 0;
        m_number = rr.m_number;
        return *this;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    添加上述代码后,输出结果如下:

    constructor!
    move constructor!
    destructor!
    destructor!
    
    • 1
    • 2
    • 3
    • 4

    此时第二次调用的就是移动构造,这样可以直接使用右值,避免重新申请空间,调用两次析构是因为,临时对象是被延长了声明周期,但最终也是要释放的。

    1.1 std::move

    前面看到移动构造接收的是右值引用,那么在需要对左值进行移动语义的时候(进行移动语义后,此左值以后将失效),那么就必须将左值转换为右值。此时td::move就很好的完成了这件事情,示例如下:

    int main()
    {
        vector<int> v{ 1,2,3,4 };
        // 拷贝构造
        vector<int> v1 = v;
        // 移动构造
        vector<int> v2 = std::move(v);
    
        cout << "v size():" << v.size() << "\n";
        cout << "v2 size():" << v2.size() << "\n";
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上面输出代码为:

    v size():0
    v2 size():4
    
    • 1
    • 2

    关于std::move注意的几点:

    • std::move本质上只是将传入的参数转换为一个右值,使用static_cast进行转换
    • std::move在进行类型推导时会保留形参的const属性,此时会造成一种使用失效的场景如下:
      class TestClass
      {
      public:
      	// 这么写在 VS中也会提示 	C26478 不要对常量变量使用 std::move
          TestClass(const string& str) :m_str(std::move(str)) {
              
          }
      
          string m_str;
      };
      
      int main()
      {
          string str = "sss";
          TestClass tc(str);
          cout << tc.m_str << "\n";
          // 此处应该输出空,但实际并非如此 , 两个输出都是 sss
          cout << str << "\n";
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
  • 相关阅读:
    【Linux】进程间通信
    图像超分经典网络 SRGAN精确解析
    FAlphaBlend——Unreal中的插值助手
    uboot指令笔记
    HMI 出色的 UI 风格
    可解释的图像分类,提高组织表征的可信度论文速读
    Module build failed (from ./node_modules/postcss-loader/src/index.js):
    盘点 Java 的那些个锁呦!
    凌恩客户文章|Nature子刊-水体RNA宏病毒组
    git学习使用
  • 原文地址:https://blog.csdn.net/weixin_41111116/article/details/126452636