23

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

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

int X::i = 0;

为什么静态数据成员(这里是“i”)只是一个声明,而不是一个定义?

4

7 回答 7

45

区分表示其初始值是什么的初始化程序和定义是很重要的。这个修改后的代码是有效的,在类定义中有初始化器:

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

const int X::i;                 // definition

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

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

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

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

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

extern你也可以像声明一个变量一样看待它:

namespace X {
  extern int i;
}

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

int X::i = 0;
于 2012-07-02T21:16:26.503 回答
7

您需要为静态数据成员(如果它的odr-used,如 C++11 中定义的那样)提供单独的定义,因为该定义应驻留在某处 - 在一个且只有一个翻译单元中。静态类数据成员基本上是在类范围内声明的全局对象(全局变量)。编译器希望您选择一个特定的翻译单元来保存每个全局对象的实际“主体”。您必须决定将实际对象放置到哪个翻译单元。

于 2012-07-02T20:43:50.883 回答
3

“静态”类成员就像一个全局分配的变量(它与单个类实例无关),因此它必须像任何符号一样驻留在某个目标文件中(并在“.cpp”文件中声明)全局变量。

简单类成员(非静态)驻留在为类实例分配的内存块中。

于 2012-07-02T20:32:58.070 回答
2

原因很简单,因为类通常在文件中声明,而这些头文件通常包含在多个 cpp 文件中。静态数据成员具有外部链接,并且必须在一个翻译单元中声明,这使得它们不适合在类中定义。

正如 juanchopanza 指出的那样,以下是允许的:

struct A
{
    const static int i = 1;
};

然而,这只是一个声明而不是一个定义。如果您要在i某处使用 's 地址,您仍然需要定义它。例如:

f(int);
g(int&);

X<A::i> x; // Okay without definition for template arguments
char a[A::i]; // Okay without definition, just using value as constant expression
&A::i; // Need a definition because I'm taking the address
f(A::i); // Okay without definition for pass by value
g(A::i); // Need a definition with pass by reference
于 2012-07-02T20:50:44.970 回答
1

当编译器从一个单元(极端简化:一个 cpp 文件及其所有包含的头文件)生成二进制代码时,它将为静态变量发出一个符号,并最终为该变量发出初始化代码。

静态变量符号可以在多个单元中声明,但不能多次初始化。

因此,您必须确保仅针对单个单元发出初始化代码。这意味着静态变量必须在一个单元中定义。

于 2012-07-02T20:41:28.053 回答
1

请记住,如果静态数据成员是 const 枚举类型的 const 整数类型,则可以在声明时对其进行初始化:

来自 C++03 标准,§9.4.2

如果静态数据成员是 const 整型或 const 枚举类型,则其在类定义中的声明可以指定一个常量初始化器,该常量初始化器应为整型常量表达式 (5.19)

struct Foo {
  static const int j = 42; // OK
};
于 2012-07-02T20:53:30.597 回答
0

静态数据成员

#include<iostream.h>
#include<conio.h>

class static_var
{

static int count; //static member of class
public :

void incr_staticvar()
{
count++;
}

void outputc()
{ 
cout<<"Value of Static variable Count :- "<<count<<endl;
}
};

int static_var::count;

void main()
{
clrscr();
static_var obj1,obj2,obj3,obj4;

obj1.incr_staticvar();
obj2.incr_staticvar();
obj3.incr_staticvar();
obj4.incr_staticvar();

cout<<"\nAfter Increment of static variable by Four Different objects is :-\n";

obj1.outputc ( );
obj2.outputc ( );
obj3.outputc ( );
obj4.outputc ( );

getch();
}
于 2014-04-20T14:02:12.473 回答