zoukankan      html  css  js  c++  java
  • c++ 右值引用&std::move()&移动构造函数&移动赋值函数

    右值引用

    所谓右值引用就是必须绑定到右值的引用,我们通过 && 而不是 & 来获得右值引用。

    右值引用有一个很重要的性质--只能绑定到一个将要销毁的对象,即左值持久,右值短暂。

      int i = 42;
      int& r = i; // 正确,r 引用 i
      int&& rr = i; // 错误,不能将一个右值引用绑定到一个左值上
      int& r2 = i * 42; // 错误,i * 42 是一个右值
      const int& r3 = i * 42; // 正确,可以将一个 const 的引用绑定到一个右值上
      int&& rr2 = i * 42; // 正确,将 rr2 绑定到乘法结果上

    标准库 move 函数

    虽然不能将一个右值引用直接绑定到一个左值上,但我们可以显式地将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为 move 的新标准函数来获得绑定到左值的右值引用。

     int&& rr3 = std::move(rr1); // ok

    move 调用告诉编译器:我们有一个左值,但我们希望像一个右值一样处理它,我们必须认识到,调用 move 就意味着:除了对 rr1 赋值或销毁它以外,我们将不再使用它,在调用 move 之后, 我们不能对移后源对象的值做任何假设。

    移动构造函数&移动赋值运算符

    移动构造函数和移动赋值运算符“窃取”资源,它们通常不分配任何资源。因此这两类函数通常不会抛出任何异常,当不抛出异常时必须声明为 noexcept。

      Foo(Foo&& v) noexcept { // 移动构造函数
          ; // TODO
      }
      Foo& operator = (Foo&& a) noexcept { // 移动操作运算符
          ; // TODO
          return *this;
      }

    用拷贝构造函数代替移动构造函数几乎肯定是安全的,用赋值运算符代替移动赋值运算符也几乎肯定是安全的。

      Foo x;
      Foo y(x); // 拷贝构造函数,x是一个左值
      Foo z(std::move(x)); // 拷贝构造函数,因为未定义移动构造函数

    在对 z 进行初始化时,我们调用了 move(x),它返回一个绑定到 x 的 Foo&&。Foo 拷贝构造函数是可行的,因为我们可以将一个 Foo&& 转换为一个 const Foo&。因此 z 的初始化将使用 Foo 的拷贝构造函数。

    引用限定符

    用来指出一个非 static 成员函数可以用于左值或右值的符号。限定符 & 和 && 应该放在参数列表之后或 const 之后(如果有的话)。被 & 限定的函数只能用于左值,被 && 限定的函数只能用于右值。

      Foo x;
      Foo y(x); // 拷贝构造函数,x是一个左值
      Foo z(std::move(x)); // 拷贝构造函数,因为未定义移动构造函数

    代码示例

      class A {
      public:
          int val = 0;
          A() {
              std::cout << "A()" << std::endl;
          }
          A(int v) {
              std::cout << "A(int v)" << std::endl;
          }
          A& operator = (const A& a) {
              std::cout << "A& operator = (const A& a)" << std::endl;
              return *this;
          }
          A(const A& a) {
              std::cout << "A(const A& a)" << std::endl;
          }
          A& operator = (A&& a) {
              std::cout << "A& operator = (A&& a)" << std::endl;
              return *this;
          }
          A(A&& v) {
              std::cout << "A(A&& v)" << std::endl;
          }
          virtual ~A() {
              std::cout << "~A()" << std::endl;
          }
          void print(int a) && { // 如果只有此函数,c.print(3);编译报错
              std::cout << "print(int a) &&" << std::endl;
          }
          void print(int a) & { // 如果只有此函数,std::move(d).print(2);编译报错
              std::cout << "print(int a) &" << std::endl;
          }
      };
      ​
      int main()
      {
          A b; // A()
          A c; // A()
          A d; // A()
      ​
          c = b; // A& operator = (const A& a)
          d = std::move(b); // A& operator = (A&& a)
      ​
         std::move(d).print(2); // print(int a) &&
         c.print(3); // print(int a) &
    // ~A()
          // ~A()
          // ~A()
          return 0;
      }

     

  • 相关阅读:
    [翻译] 编写高性能 .NET 代码--第五章 通用编码与对象设计 -- 类 vs 结构体
    [翻译] 编写高性能 .NET 代码--第二章 GC -- 配置选项
    [翻译]编写高性能 .NET 代码 第二章:垃圾回收 基本操作
    [翻译]编写高性能 .NET 代码 第二章:垃圾回收
    [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio
    [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)
    [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 平均值 vs 百分比
    [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 选择什么来衡量
    NGUI锚定系统:UIAnchorUIRect
    NGUI Panel裁剪、层级实现原理
  • 原文地址:https://www.cnblogs.com/tongyishu/p/13414148.html
Copyright © 2011-2022 走看看