【问题标题】:Static Data Member Initialization静态数据成员初始化
【发布时间】:2012-07-02 20:24:41
【问题描述】:

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

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

int X::i = 0;

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

【问题讨论】:

  • 因为。 ;) 这就是语言的定义方式。 ;)
  • 你被允许static const int i = 10
  • @CharlesBeattie,这还不是定义,有时需要定义,见gcc.gnu.org/wiki/…

标签: c++ c++11 g++


【解决方案1】:

区分 initializer定义 很重要,initializer 说明了它的初始值是什么。这个修改后的代码是有效的,类定义中有初始化器:

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;

【讨论】:

  • 如果它是静态数据成员,i 是否不必是 constconstexpr 才能工作?
  • 怀疑 - 每次你创建一个新的 X 时,你也会创建一个新的 X::i 变量。不应该是这样的“每次你创建一个新的X,你也创建了一个新的X::normalValue变量”。
  • @Rndp13,不确定我是在暗示“如果i 是非静态的,那么......”或者我只是愚蠢,我已将其更改为normalValue,因为那是更清楚,谢谢。
  • "Every time you create a new X you also create a new X::normalValue variable." 那根据你自己的说法是错误的answer
【解决方案2】:

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

【讨论】:

  • 我认为这是使用 c++11 进行的非静态数据成员初始化。
  • 我从我的回答中删除了不正确的“在 C++11 中所有静态成员也可以有初始化程序”声明。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

    正如 juanchopanza 指出的那样:

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

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

    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
    

    【讨论】:

      【解决方案5】:

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

      来自 C++03 标准,§9.4.2

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

      struct Foo {
        static const int j = 42; // OK
      };
      

      【讨论】:

      • 但即便如此,如果变量是 odr-used,您仍然需要定义。
      【解决方案6】:

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

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

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

      【讨论】:

      • 不,您将初始化程序与定义混淆了。初始化器可以在每个 TU 中重复的声明中给出。定义(带或不带初始化器)只能存在于一处。
      • 我发现考虑地址更有用,而不是考虑初始化。静态变量必须有地址,而且只有一个地址,所以它必须定义在一个翻译单元中,这样链接器才能给它一个地址。
      【解决方案7】:

      静态数据成员

      #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-09-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-02
        • 2011-07-18
        • 2015-05-18
        相关资源
        最近更新 更多