翻译单元和链接

在 C++ 程序中,符号(例如变量或函数名称)可以在其范围内进行任意次数的声明。 但是,一个符号只能被定义一次。 这就是“单一定义规则”(ODR)。 声明在程序中引入(或重新引入)一个名称,以及足够的信息,以便以后将该名称与定义联系起来。 定义引入一个名称,并提供创建它所需的全部信息。 如果名称表示变量,则定义会显式创建存储并进行初始化。 函数定义由签名和函数体组成。 类定义由类名和一个列出所有类成员的块组成。 (成员函数体可以选择在另一个文件中单独定义。)

下面的示例演示了一些声明:

int i;
int f(int x);
class C;

下面的示例演示了一些定义:

int i{42};
int f(int x){ return x * i; }
class C {
public:
   void DoSomething();
};

一个程序包括一个或多个翻译单元。 一个翻译单元由一个实现文件及其直接或间接包含的所有标头组成。 实现文件通常具有文件扩展名 .cpp.cxx。 头文件通常具有扩展名 .h.hpp。 每个翻译单元由编译器独立编译。 编译完成后,链接器会将编译后的翻译单元合并到单个程序中。 ODR 规则的冲突通常显示为链接器错误。 在多个翻译单元中定义同一名称时,将发生链接器错误。

通常,使变量在多个文件中可见的最佳方式是在头文件中声明它。 然后,在需要声明的每个 .cpp 文件中添加一个 #include 指令。 通过在标头内容周围添加 include 防范,可以确保标头声明的名称对每个翻译单元只声明一次。 仅在一个实现文件中定义名称。

在 C++20 中,模块作为头文件的改进替代方法引入。

在某些情况下,可能需要在 .cpp 文件中声明全局变量或类。 在这些情况下,你需要一种方法来告知编译器和链接器名称所具有的链接类型。 链接的类型指定对象的名称是仅在一个文件中可见,还是在所有文件中可见。 链接的概念仅适用于全局名称。 链接的概念不适用于在一定范围内声明的名称。 范围是由一组封闭的大括号指定的,例如在函数或类的定义中。

外部链接与内部链接

Free 函数是在全局范围或命名空间范围内定义的函数。 默认情况下,非常量全局变量和 Free 函数具有外部链接;它们在程序中的任何翻译单元内可见。 其他任何全局对象都不能具有该名称。 具有内部链接或无链接的符号仅在声明它的翻译单元内可见。 当一个名称具有内部链接时,同一名称可能存在于另一个翻译单元中。 类定义或函数体中声明的变量没有链接。

如果要强制一个全局名称具有内部链接,可以将它显式声明为 static。 此关键字将它的可见性限制在声明它的同一翻译单元内。 在此上下文中,static 表示与应用于局部变量时不同的内容。

默认情况下,以下对象具有内部链接:

  • const对象
  • constexpr对象
  • typedef对象
  • 命名空间范围中的 static 对象

若要为 const 对象提供外部链接,请将其声明为 extern 并为其赋值:

extern const int value = 42;

有关详细信息,请参阅 extern

另请参阅

基本概念