zoukankan      html  css  js  c++  java
  • 右值引用

    对象移动

    新标准的一个最主要的特性是可以移动而非拷贝对象的能力。很多情况下都会发生对象拷贝。在其中某些情况下,对象拷贝后就立即被销毁了。在这些情况下,移动而非拷贝对象会大幅度提升性能。

    在重新分配内存的过程中,从旧内存将元素拷贝到新内存是不必要的,更好的方式是移动元素。使用移动而不是拷贝的另一个原因源于IO类或unique_ptr这样的类。这些类都包含不能被共享的资源(如指针或IO 缓冲)。因此,这些类型的对象不能拷贝但可以移动。

    在旧 C++标准中,没有直接的方法移动对象。因此,即使不必拷贝对象的情况下,我们也不得不拷贝。如果对象较大,或者是对象本身要求分配内存空间(如 string),进行不必要的拷贝代价非常高。类似的,在旧版本的标准库中,容器中所保存的类必须是可拷贝的。但在新标准中,我们可以用容器保存不可拷贝的类型,只要它们能被移动即可。

    右值引用

    为了支持移动操作,新标准引入了一种新的引用类型——右值引用(rvalue reference)。所谓右值引用就是必须绑定到右值的引用。我们通过&&而不是&来获得右值引用。

    如我们将要看到的,右值引用有一个重要的性质——只能绑定到一个将要销毁的对象。因此,我们可以自由地将一个右值引用的资源"移动"到另一个对象中。

    回忆一下,左值和右值是表达式的属性。一些表达式生成或要求左值,而另外一些则生成或要求右值。

    一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的

    类似任何引用,一个右值引用也不过是某个对象的另一个名字而已

    如我们所知,对于常规引用(为了与右值引用区分开来,我们可以称之为左值引用(lvalue reference),我们不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式。

    右值引用有着完全相反的绑定特性∶我们可以将一个右值引用绑定到这类表达式上,但不能将一个右值引用直接绑定到一个左值上∶

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

    返回左值引用的函数,连同赋值、下标、解引用和前置递增/递减运算符,都是返回左值的表达式的例子。我们可以将一个左值引用绑定到这类表达式的结果上。

    返回非引用类型的函数,连同算术、关系、位以及后置递增/递减运算符,都生成右值。

    我们不能将一个左值引用绑定到这类表达式上,但我们可以将一个 const 的左值引用或者一个右值引用绑定到这类表达式上。

    左值持久;右值短暂

    考察左值和右值表达式的列表,两者相互区别之处就很明显了:左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。

    由于右值引用只能绑定到临时对象,我们得知

    • 所引用的对象将要被销毁
    • 该对象没有其他用户

    这两个特性意味着∶使用右值引用的代码可以自由地接管所引用的对象的资源。

    变量是左值

    变量可以看作一个只有运算对象而没有运算符的表达式,虽然我们很少这么看待变量。类似其他任何表达式,变量表达式也有左值/右值属性。

    变量表达式都是左值。带来的结果就是,我们不能将一个右值引用绑定到一个右值引用类型的变量上,这有些令人惊讶∶

    int &srr1 = 42; // 正确∶ 字面常量是右值 
    int &&rr2 = rrl; // 错误∶ 表达式 rr1 是左值!
    

    其实有了右值表示临时对象这一观察结果,变量是左值这一特性并不令人惊讶。毕竟,变量是持久的,直至离开作用域时才被销毁。

    标准库 move 函数

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

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

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

    我们可以销毁一个移后源对象,也可以赋予它新值,但不能使用一个移后源对象的值。

    如前所述,与大多数标准库名字的使用不同,对move我们不提供using声明(参见3.1节,第74页)。我们直接调用std∶∶move而不是move。

  • 相关阅读:
    了解基本的bash shell命令
    实验七:Xen环境下cirrOS的安装配置
    实验六:通过grub程序引导本地磁盘内核启动系统(busybox)
    实验五:Xen环境下多虚拟机的桥接配置
    实验四:xl命令的常见子命令以及操作
    实验三:xen环境下的第一个虚拟机的安装
    练习1
    Linux网站运维工程师基础大纲
    实验二:Linux下Xen环境的安装
    实验一:通过bridge-utils工具创建网桥并实现网络连接
  • 原文地址:https://www.cnblogs.com/lihello/p/14452558.html
Copyright © 2011-2022 走看看