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

Why static variable needs to be explicitly defined?

本文关键字:定义 静态 变量 为什么      更新时间:2023-10-16

在类中:

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

我们必须明确定义静态变量,否则它将导致

undefined reference to 'foo::bar'

我的问题是:

为什么我们必须给出静态变量的明确定义


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

从一开始,C++语言就和C一样,建立在独立翻译的原则上。每个翻译单元都由编译器本身独立编译,而不了解其他翻译单元。整个程序只有在连接阶段才能结合在一起。链接阶段是链接器看到整个程序的最早阶段(它被视为编译器本身准备的对象文件的集合)。

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

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

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

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


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

在早期的C++中,允许在类内定义static数据成员,这肯定违反了类只是一个蓝图而不留出内存的想法。此项现已删除。

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

static是一种存储类型,当您声明要告诉编译器"本周在数据部分的某个地方"的变量时,当您随后使用它时,编译器会发出从TBD地址加载值的代码。

在某些情况下,编译器可以驱动静态实际上是一个编译时间常数,并用它来代替它,例如

static const int meaning = 42;

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

然而,在处理类成员时,编译器无法猜测应该在哪里创建该值。它可能位于要链接的库或dll中,也可能提供的库中的值必须由库使用者提供。

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

如果你想要我们一个恒定值,例如

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

你最好使用以下中的一种或另一种

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

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

在类中,您只声明变量,即:您告诉编译器有这个名称。然而,一个静态变量必须有一些内存空间,并且必须在一个翻译单元内。编译器仅在定义变量时保留此空间。

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

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