章节目录

内部链接

本节阅读量:

在局部变量一节中,我们说过,“标识符的链接属性决定了该名称的其他声明是否引用同一对象”,同时也得知–局部变量没有链接属性。

全局变量和函数标识符可以具有内部链接或外部链接属性。我们将在本课中介绍内部链接的情况,在下一节中介绍外部链接。

具有内部链接的标识符可以在单个翻译单元内看到并使用,但它不能从其他翻译单元访问(即,它不向链接器公开)。这意味着,如果两个源文件具有具有内部链接的同名标识符,则这些标识符将被视为独立的(并且不会因具有重复定义而导致ODR冲突)。


具有内部链接的全局变量

具有内部链接的全局变量有时称为内部变量。

为了使非常量的全局变量成为内部变量,使用static关键字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <iostream>

static int g_x{}; // 非常量的全局变量默认是外部链接, 但可以通过 static 关键字使它成为内部链接

const int g_y{ 1 }; // const 全局变量默认是内部链接
constexpr int g_z{ 2 }; // constexpr 全局变量默认是内部链接

int main()
{
    std::cout << g_x << ' ' << g_y << ' ' << g_z << '\n';
    return 0;
}

默认情况下,Const和constexpr全局变量具有内部链接属性(因此不需要static关键字——如果使用static关键字也无额外作用)。

下面是使用内部变量的多个文件的示例:

a.cpp:

1
constexpr int g_x { 2 }; // 这里的内部链接 g_x 只在 a.cpp 中能被访问

main.cpp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <iostream>

static int g_x { 3 }; // 这里的内部链接 g_x is 只在main.cpp 中能被访问

int main()
{
    std::cout << g_x << '\n'; // 使用 main.cpp 中的 g_x, 打印 3

    return 0;
}

该程序打印:

1
3

因为g_x是每个文件的内部变量,所以main.cpp不知道a.cpp也有一个名为g_x的变量(反之亦然)。


具有内部链接的函数

由于链接是标识符的属性(不是变量的属性),函数标识符也具有链接属性。函数默认为外部链接(我们将在下一课中介绍),但可以通过static关键字设置为内部链接:

add.cpp:

1
2
3
4
5
6
// 这个函数被声明为 static, 因此只能在本文件中被使用
// 即是其它文件中有add函数的前向声明,但也仍然不能访问到add函数
static int add(int x, int y)
{
    return x + y;
}

main.cpp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <iostream>

static int add(int x, int y); // 前向声明 add

int main()
{
    std::cout << add(3, 4) << '\n';

    return 0;
}

此程序无法链接成功,因为在add.cpp之外无法访问函数add。


单定义规则和内部链接

根据单定义规则,我们注意到,对象或函数不能在文件或程序中具有多个定义。

然而,值得注意的是,在不同文件中定义的内部对象(和函数)被认为是独立的实体(即使它们的名称和类型相同),因此不会违反单定义规则。每个文件内部对象只有一个定义。


static 对比 未命名的命名空间

在现代C++中,使用static关键字为标识符提供内部链接越来越不受欢迎。未命名的名称空间可以为更广泛的标识符(例如,类型标识符)提供内部链接,并且它们更适合为多个标识符提供内部链接。

在后续,会讨论未命名的名称空间。


为什么要费心为标识符提供内部链接?

让标识符具有内部链接通常有两个原因:

  1. 有一个标识符,我们要确保其他文件无法访问。这可能是一个我们不想弄乱的全局变量,或者是一个不想调用的辅助函数。
  2. 避免命名冲突。由于具有内部链接的标识符不会向链接器公开,因此它们只能与同一翻译单元中的名称发生冲突,而不会在整个程序中发生冲突。

许多现代开发指南建议,不打算在其它文件中访问的变量或函数,都设置为内部链接。如果你可以完全遵守,这是一个很好的建议。

当然,有一个更为轻量级的准则:将有明确理由不允许从其他文件访问的任何标识符设置为内部链接。


快速摘要

1
2
3
4
5
6
7
8
9
// 全局变量 内部链接 定义:
static int g_x;          // 定义未初始化的 内部全局变量 (默认初始化为0)
static int g_x{ 1 };     // 定义初始化了的 内部全局变量

const int g_y { 2 };     // 定义初始化了的 const 内部全局变量
constexpr int g_y { 3 }; // 定义初始化了的 constexpr 内部全局变量

// 函数 内部链接 定义:
static int foo() {};     // 定义 内部 函数

7.4 变量名称遮挡(Variable shadowing)

上一节

7.6 外部链接和变量前向声明

下一节