为什么需要显式定义静态变量?

Why static variable needs to be explicitly defined?

在class中:

class foo
{
public:
    static int bar; //declaration of static data member
};

int foo::bar = 0; //definition of data member

我们必须显式定义静态变量,否则会导致

<strong>未定义引用 'foo::bar'</strong>

我的问题是:

为什么要给一个静态变量显式定义?


请注意,这 不是 以前问过的 undefined reference to static variable 问题的重复。本题意在询问显式定义静态变量背后的原因。

在早期的 C++ 中,允许在 class 中定义 static 数据成员,这肯定违反了 class 只是一个蓝图并且不预留内存的想法。这已经被放弃了。

static 成员的定义放在 class 之外,强调为 static 数据成员只分配一次内存(在编译时)。 class 的每个对象都没有自己的副本。

从一开始,C++ 语言就和 C 语言一样,建立在独立翻译 的原则之上。每个翻译单元都由 适当的 编译器独立编译,不了解其他翻译单元。整个程序只是在稍后的链接阶段才组合在一起。链接阶段是 整个 程序被 链接器 看到的最早阶段(它被视为由 正确的编译器).

为了支持独立翻译这一原则,每个具有外部链接的实体必须在一个翻译单元中定义,并且只能在一个翻译单元中定义。用户负责在不同翻译单元之间分配此类实体。它被认为是 用户意图 的一部分,即用户应该决定哪个翻译单元(和目标文件)将包含每个定义。

这同样适用于 class 的静态成员。 class 的静态成员是具有外部链接的实体。编译器希望您在某个翻译单元中定义该实体。此功能的全部目的是让您有机会选择那个翻译单位。编译器无法为您选择它。同样,这是您意图的一部分,您必须告诉编译器。

这不再像以前那样重要,因为现在的语言旨在处理(并消除)大量相同的定义(模板、内联函数等),但是一个定义规则仍然植根于独立翻译的原则。

除上述之外,在 C++ 语言中,您定义变量的位置将决定其相对于同一翻译单元中定义的其他变量的初始化顺序。这也是 用户意图 的一部分,也就是说,如果没有您的帮助,编译器无法做出决定。


从 C++17 开始,您可以将静态成员声明为 inline。这消除了对单独定义的需要。通过以这种方式声明它们,您可以有效地告诉编译器您不关心该成员的物理定义位置,因此也不关心其初始化顺序。

在 class 中你只是声明变量,即:你告诉编译器有这个名字的东西。 但是,静态变量必须获得一些内存 space 才能存在,并且这必须在一个翻译单元内。编译器仅在您定义变量时保留此 space。

static 是一种存储类型,当您声明变量时,您是在告诉编译器 "this week be in the data section somewhere" 并且当您随后使用它时,编译器会发出从 TBD 地址加载值的代码。

在某些情况下,编译器可以驱动 static 实际上是一个编译时间常量并将其替换为例如

static const int meaning = 42;

在从不获取值地址的函数中。

然而,在处理 class 成员时,编译器无法猜测应该在何处创建该值。它可能在您将 link 反对的库中,或一个 dll,或者您可能正在提供一个库,其中值必须由库使用者提供。

通常,当有人问这个问题时,是因为他们滥用了静态成员。

如果你想要我们一个常量值,例如

static int MaxEntries;
...
int Foo::MaxEntries = 10;

您最好选择以下其中一项

static const int MaxEntries = 10;
 // or
enum { MaxEntries = 10 };

静态不需要单独的定义,直到某些东西试图获取变量的地址或形成对变量的引用,枚举版本从不这样做。

结构不是可变的,但它的实例是。因此我们可以在多个模块中包含相同的结构声明,但是我们不能在多个模块中全局定义相同的实例名称

结构体的静态变量本质上是一个全局变量。如果我们在结构声明本身中定义它,我们将无法在多个模块中使用结构声明。因为这会导致在多个模块中定义相同的全局实例名称(静态变量)导致链接器错误 "Multiple definitions of same symbol"