生命周期

字面量

字面量(literal),顾名思义:字面上的量,是指直接写入代码的数值。使用二进制编辑器查看二进制文件时可以直接看到。

HINT: 字符串字面量是一个例外。当字符串字面量存在于类中时,它是一个右值,否则为左值

关于字面量的分类和用法参见:

左值、右值

关于左值和右值的定义可以简要概括如下:

  • 凡是可以被取地址的都是左值(充要条件)

  • 凡是不可被取地址的都是右值(充要条件)

  • 产出左值的表达式是左值表达式

  • 产出右值的表达式是右值表达式

下面对左值和右值进一步分类:[1]

Diagram
  • 左值( lvalue (left value) )指明(designates)了一个函数或对象。(假设 E 是一个函数指针,那么 *E 是一个左值)

  • 将亡值( xvalue (eXpiring value) )指明了一个生命周期即将结束的对象(因此它可能被移动)。某些将亡值是由于右值参与运算的结果。

  • 泛左值( glvalue (generalized lvalue) )是一个左值或者将亡值

  • 右值( rvalue (right value) )是将亡值或无名对象

  • 纯右值( prvalue (pure rvalue) )是非将亡值的右值

左值

特征:

  • 可取地址

  • 左值指向了一个对象

  • 左值具备泛左值的所有特征

以下表达式为左值:

  • 变量、函数和数据成员

  • 返回类型为左值引用的表达式

  • 返回类型可被取地址的表达式。(例如 a = b*p

  • 非类中的字符串字面量

  • 强制类型转换为左值引用的表达式。(例如 static_cast<int&>(x)

  • 返回类型为左值引用的表达式(例如强制类型转换、函数调用、转换为右值引用的函数指针)

  • 对类对象的操作(例如 a[n]a.mp→mp→*m,此时要求类对象为左值)

  • 对数组的操作(例如 a[n],此时要求数组为左值)

什么是转换为右值引用的函数指针?

static_cast<void (&&)(int) >(x);
  • 由于 std::endlstd::cout 等也是对象,可以被取地址,因此它们也是左值表达式。

  • “位于表达式的左边” 在C++11 的值分类不是左值的必要条件,例如 a = b = cb = c 位于表达式的右边

纯右值

特征:

  • 纯右值不可能被取地址

  • 纯右值没有多态

  • 非类非数组类型的纯右值不能被cv限定(const-volatile qualifiers)[2]

  • 纯右值具备右值的所有特征

  • 纯右值不能是不完整的类型[3] (void类型除外)

  • 纯右值不能包含抽象类或数组

以下表达式为右值表达式

  • 非字符串字面量的字面量

  • 返回类型不是引用的函数调用(例如 std::string getName()

  • 所有返回临时对象的表达式(算数运算符、逻辑运算符、&a)

  • 所有无法取地址的对象(如 this指针lambda 表达式):

    • a.m 。 其中m是成员枚举或非静态成员函数,或a是左值, m既不是引用类型也不是静态成员

    • a→m 。其中m是成员枚举或者非静态成员函数

    • a.*mp。 其中mp是指向成员函数的指针或者a是一个右值,mp是指向成员变量的指针

  • 枚举

  • 对于涉及到成员的表达式:a.mp→ma.*mpp→*mp,返回的类型即可能为左值也可能为纯右值。

  • 请注意: a` 是左值,而 `a 是右值。你可以尝试自己创建一个类,重载前置和后置运算符来发现其中的magic。

将亡值

特征:

将亡值同时具备右值引用和泛左值的特征,尤其是将亡值可以像右值那样绑定到右值引用,又可以像泛左值那样具备多态。非类的将亡值可以被cv限定。

以下为将亡值表达式

  • 返回类型为右值引用的函数调用(如 std::move(x)

  • 从右值对象获取的属性(例如 a[m]a.*mp,此时a为右值)

  • 转换对象为右值引用的表达式(例如 static_cast<char&&>(x)

泛左值

泛左值要么是将亡值,要么是左值

特征:

右值

右值要么是将亡值,要么是纯右值

  • 右值无法被取地址

  • 右值可以被用于初始化 被const限定的左值引用 ,此时右值的生命周期将被延长至与左值相同

  • 一个右值可以用于初始化右值引用,此时其生命周期将被延长至与右值引用相同

  • 在选择重载函数时,右值将倾向于右值引用,而不是 被const限定的左值引用

  • 类中的字符串字面量

  • Value categories

悬空引用

当引用指向对象的生命周期结束时,对引用的访问是未定义行为:

std::string& f()
{
   std::string s = "Example";
   return s; // 退出 s 的作用域:
            // 调用其析构函数并解分配其存储
}

std::string& r = f(); // 悬垂引用
std::cout << r;       // 未定义行为:从悬垂引用读取
std::string s = f();  // 未定义行为:从悬垂引用复制初始化

1. ISO/IEC 14882:2011 chap. 3.10 (C++11 ISO标准文件)
Last moify: 2023-12-15 04:28:35
Build time:2025-07-18 09:41:42
Powered By asphinx