静态数据成员初始化

Cyb*_*Guy 19 c++ g++ c++11

为什么静态数据成员初始化必须在类之外?

class X
{
public:
      int normalValue = 5; //NSDMI
      static int i;
};

int X::i = 0;
Run Code Online (Sandbox Code Playgroud)

为什么静态数据成员(此处为"i")只是一个声明,而不是一个定义?

Jon*_*ely 37

重要的是区分初始化器,它表示初始值是什么,以及定义.这个修改过的代码是有效的,初始化器在类定义中:

class X
{
public:
  int normalValue = 5;
  static const int i = 0;       // declaration, with initializer
};

const int X::i;                 // definition
Run Code Online (Sandbox Code Playgroud)

即,必须在类外面的是定义,而不是初始化.

那是因为变量必须在内存中有一个地址(除非它仅在有限的情况下使用,例如在编译时常量表达式中).

非静态成员变量存在于它所属的对象内部,因此其地址取决于包含它的对象的地址.每次创建新的时,X您还会创建一个新X::normalValue变量.非静态数据成员的生命周期从类的构造函数开始.NSDMI语法与内存中的变量地址没有任何关系,它只允许您在一个地方提供初始值,而不是在每个构造函数中使用显式构造函数初始化列表重复它.

另一方面,静态成员变量不包含在类的实例中,它独立于任何单个实例而存在,并且从程序的开头存在于固定地址.为了使静态成员变量(或任何其他全局对象)获得唯一的地址,链接器必须只在一个目标文件中看到静态变量的一个定义,并为其分配一个地址.

因为静态变量只需要在一个目标文件中只有一个定义,所以允许在类中提供该定义是没有意义的,因为类定义通常存在于头文件中并包含在多个目标文件中.因此,虽然您可以在类中提供初始化程序,但仍需要在某处定义静态数据成员.

您也可以像声明extern变量一样查看它:

namespace X {
  extern int i;
}
Run Code Online (Sandbox Code Playgroud)

这声明了变量,但程序中必须有一个定义:

int X::i = 0;
Run Code Online (Sandbox Code Playgroud)


AnT*_*AnT 7

您需要为静态数据成员提供单独的定义(如果其使用的odr,如C++ 11中所定义),因为该定义应驻留在某个地方 - 仅在一个转换单元中.静态类数据成员基本上是类范围中声明的全局对象(全局变量).编译器希望您选择一个特定的转换单元来保存每个全局对象的实际"主体".您必须决定将实际对象放在哪个翻译单元中.