作用域、持续时间和链接这些概念很绕人,所以我们利用这节课对它们进行一次整理和总结。有些内容我们还没有介绍,但是我们仍然将其罗列在这里,在后续学习时可以进行参考。

作用域小结

变量标识符的作用域决定了标识符在哪里可以被访问。

  • 具有块(局部)作用域的标识符,只能够在声明它们的块(及其嵌套块)中访问,这些变量包括:
  • 具有文件(全局)作用域 的变量和函数从声明位置开始,直到文件结尾都可以被访问, 包括:
    • 全局变量
    • 函数
    • 定义在全局作用域或命名空间中的用户自定义类型(包括枚举和类)

持续时间小结

变量的持续时间决定了它合适被创建和销毁。

链接小结

标识符的链接属性,决定了标识符的多个声明是否被看做同一个表示符。

  • 不具备链接属性的标识符,意味着该标识符只表示它自己,包括:
    • 局部变量
    • 定义在块中的用户自定义类型(包括枚举和类)
  • **具备内部链接属性****的标识符可以在文件内部的任何位置被访问,包括:
    • 静态全局变量(初始化或未初始化的)
    • 静态(static)函数
    • const 全局变量
    • 定义在匿名命名空间中的函数
    • 定义在匿名命名空间中的用户自定义类型(包括枚举和类)
  • 具备外部链接属性的标识符可以在定义它的文件中访问,也可以在其他文件中(通过前向声明)访问,包括:
    • 函数
    • 非常量全局变量(初始化或未初始化的)
    • 外部常量全局变量
    • 内联常量全局变量
    • 定义在命名空间或全局作用域中的用户自定义类型(包括枚举和类)

具有外部链接的标识号如果在多个cpp文件中被编译,则会导致链接器报错(因为违反了单一定义规则(one-definition-rule))。不过,也有例外的情况(对于类型、目标、内联函数和内联变量)——我们会在后续的课程中介绍相关内容。

同时,注意函数默认具有外部链接,必须要使用 static 才能使其具有内部链接。

变量作用域、持续时间和链接小结

因为变量具有作用域、持续时间和链接属性,这里我们使用表格对其进行总结:

类型例子作用域持续时间链接属性备注
局部变量int x;自动
静态局部变量static int s_x;静态
动态变量int *x { new int{} };动态
函数形参void foo(int x)自动
外部非常量全局变量int g_x;文件静态外部初始化或未初始化
内部非常量全局变量static int g_x;文件静态内部初始化或未初始化
内部常量全局变量constexpr int g_x { 1 };文件静态内部必须初始化
外部常量全局变量extern const int g_x { 1 };文件静态外部必须初始化
内联常量全局变量(C++17)inline constexpr int g_x { 1 };文件静态外部必须初始化

前向声明小结

使用前向声明可以从其他文件中访问一个函数或者变量。声明变量的作用域与往常一样(全局变量具有文件作用域,局部变量具有块作用域)。

类型例子备注
函数前向声明void foo(int x);只需要函数原型,不能有函数体
非常量的变量前向声明extern int g_x;必须未初始化
常量的前向声明extern const int g_x;必须未初始化
Constexpr 变量前向声明extern constexpr int g_x;不允许,constexpr不能前向声明

存储类型说明符到底是什么?

当 static 和 extern 关键字(存储类型说明符)作为变量声明的一部分时,它们可以设定标识符的存储持续时间和链接属性。

C++ 支持 4 种存储类型说明符:

说明符含义备注
externstatic (或 thread_local) 存储持续时间和外部链接
staticstatic (或 thread_local) 存储持续时间和内部链接
thread_localthread 存储持续时间
mutable即使类是const类型的,变量也允许修改
auto自动存储持续时间在 C++11 中已经弃用
register自动存储持续时间并且提示编译器将其存放在寄存器中在 C++17 中已经弃用

术语 存储类型说明符 通常只会在正式的文档中使用。