生命周期 ######################################## 字面量 **************************************** 字面量(literal),顾名思义:字面上的量,是指直接写入代码的数值。使用二进制编辑器查看二进制文件时可以直接看到。 .. hint:: 字符串字面量是一个例外。当字符串字面量存在于类中时,它是一个右值,否则为左值 .. seealso:: 关于字面量的分类和用法参见: - `C++ 字面量 `_ - `用户定义字面量 (C++11 起) `_ 左值、右值 **************************************** 关于左值和右值的定义可以简要概括如下: - 凡是可以被取地址的都是左值(充要条件) - 凡是不可被取地址的都是右值(充要条件) - 产出左值的表达式是左值表达式 - 产出右值的表达式是右值表达式 下面对左值和右值进一步分类: [#]_ .. mermaid:: graph TD; 表达式 --> 泛左值; 表达式 --> 右值; 泛左值 --> 左值; 泛左值 --> 将亡值; 右值 --> 纯右值; 右值 --> 将亡值; - 左值( :abbr:`lvalue (left value)` )指明(designates)了一个函数或对象。(假设 E 是一个函数指针,那么 \*E 是一个左值) - 将亡值( :abbr:`xvalue (eXpiring value)` )指明了一个生命周期即将结束的对象(因此它可能被移动)。某些将亡值是由于右值参与运算的结果。 - 泛左值( :abbr:`glvalue (generalized lvalue)` )是一个左值或者将亡值 - 右值( :abbr:`rvalue (right value)` )是将亡值或无名对象 - 纯右值( :abbr:`prvalue (pure rvalue)` )是非将亡值的右值 .. [#] ISO/IEC 14882:2011 chap. 3.10 (C++11 ISO标准文件) 左值 ======================================== 特征: - 可取地址 - 左值指向了一个对象 - 左值具备泛左值的所有特征 以下表达式为左值: - 变量、函数和数据成员 - 返回类型为左值引用的表达式 - 返回类型可被取地址的表达式。(例如 ``a = b`` 、 ``*p`` ) - 非类中的字符串字面量 - 强制类型转换为左值引用的表达式。(例如 ``static_cast(x)`` ) - 返回类型为左值引用的表达式(例如强制类型转换、函数调用、转换为右值引用的函数指针) - 对类对象的操作(例如 ``a[n]``、``a.m``、``p->m``、``p->*m``,此时要求类对象为左值) - 对数组的操作(例如 ``a[n]``,此时要求数组为左值) .. note:: 什么是转换为右值引用的函数指针? .. code-block:: cpp static_cast(x); .. tip:: - 由于 ``std::endl`` 、 ``std::cout`` 等也是对象,可以被取地址,因此它们也是左值表达式。 - “位于表达式的左边” 在C++11 的值分类不是左值的必要条件,例如 ``a = b = c``, ``b = c`` 位于表达式的右边 纯右值 ======================================== 特征: - 纯右值不可能被取地址 - 纯右值没有多态 - 非类非数组类型的纯右值不能被cv限定(const-volatile qualifiers)[#]_ - 纯右值具备右值的所有特征 - 纯右值不能是不完整的类型 [#]_ (void类型除外) - 纯右值不能包含抽象类或数组 以下表达式为右值表达式 - 非字符串字面量的字面量 - 返回类型不是引用的函数调用(例如 ``std::string getName()`` ) - 所有返回临时对象的表达式(算数运算符、逻辑运算符、&a) - 所有无法取地址的对象(如 ``this指针``、``lambda`` 表达式) - a.m 。 其中m是成员枚举或非静态成员函数,或a是左值, m既不是引用类型也不是静态成员 - a->m 。其中m是成员枚举或者非静态成员函数 - a.*mp。 其中mp是指向成员函数的指针或者a是一个右值,mp是指向成员变量的指针 - 枚举 .. important:: - 对于涉及到成员的表达式:``a.m``、``p->m``、``a.*mp``、``p->*mp``,返回的类型即可能为左值也可能为纯右值。 - 请注意: ``++a`` 是左值,而 ``a++`` 是右值。你可以尝试自己创建一个类,重载前置++和后置++运算符来发现其中的magic。 .. [#] `cv (const and volatile) type qualifiers `_ .. [#] ` Incomplete type `_ 将亡值 ======================================== 特征: 将亡值同时具备右值引用和泛左值的特征,尤其是将亡值可以像右值那样绑定到右值引用,又可以像泛左值那样具备多态。非类的将亡值可以被cv限定。 以下为将亡值表达式 - 返回类型为右值引用的函数调用(如 ``std::move(x)``) - 从右值对象获取的属性(例如 ``a[m]``、``a.*mp``,此时a为右值) - 转换对象为右值引用的表达式(例如 ``static_cast(x)`` ) 泛左值 ======================================== 泛左值要么是将亡值,要么是左值 特征: - 泛左值可以被 `隐式转换到纯右值 `_ (例如 左值 -> 右值、数组 -> 指针、函数 -> 指针) - 泛左值可以拥有多态 - 泛左值允许 `不完整的类型 `_ 右值 ======================================== 右值要么是将亡值,要么是纯右值 - 右值无法被取地址 - 右值可以被用于初始化 ``被const限定的左值引用`` ,此时右值的生命周期将被延长至与左值相同 - 一个右值可以用于初始化右值引用,此时其生命周期将被延长至与右值引用相同 - 在选择重载函数时,右值将倾向于右值引用,而不是 ``被const限定的左值引用``。 - 类中的字符串字面量 .. seealso:: - `Value categories `_ 悬空引用 [#]_ **************************************** 当引用指向对象的生命周期结束时,对引用的访问是未定义行为: .. code-block:: cpp std::string& f() { std::string s = "Example"; return s; // 退出 s 的作用域: // 调用其析构函数并解分配其存储 } std::string& r = f(); // 悬垂引用 std::cout << r; // 未定义行为:从悬垂引用读取 std::string s = f(); // 未定义行为:从悬垂引用复制初始化 .. [#] `引用声明 `_