翻译单元
在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()
- 内部链接/无链接:只能在声明它的翻译单元可见
- 如果名称具有内部链接,则同一名称可能存在另一个翻译单元中
- 在类定义或者函数体中声明的变量没有链接
可以通过将全局名称显示声明为static
来强制要求使用内部链接。
默认情况下,下列对象具有内部链接
- const对象
- constexpr对象
- typedefs
- 命名空间范围内的静态对象
如果要为常量对象外部链接,请将其声明为extern
并为其赋值:
extern const int value = 42;