为什么必须在定义之外初始化非const,非int / enum静态数据成员?

时间:2013-03-01 21:34:25

标签: c++ static header

据我所知,只有static,const和int / enum(pre c ++ 11)的数据成员才能在类声明中初始化。 “所有其他静态数据成员必须在全局命名空间范围内定义(即在类定义的主体之外),并且只能在这些定义中初始化”。

为什么不能在类定义中初始化其他静态数据成员?有没有具体的原因被禁止?

如果数据成员特定于该类,为什么它们在全局命名空间范围内声明,而不是与其类相关的某些范围?

5 个答案:

答案 0 :(得分:5)

  

为什么不能在类定义中初始化其他静态数据成员?有没有具体的原因被禁止?

最有可能因为C ++有单独的翻译单元。编译器需要选择一个目标文件,其中将放置这些符号的初始化逻辑。强制将其放在特定的源文件中会使编译器轻松做出决定。

  

如果数据成员特定于该类,为什么它们在全局命名空间范围内声明,而不是与其类相关的某些范围?

因为这就是C ++如何成为班级成员。这与其他类成员(如成员函数)没有什么不同:

标题文件:

namespace example {

// Class declared in header
struct some_class
{
    // Member variable
    static float example;
    // Member function
    void DoStuff() const;
};

}

源文件:

namespace example {

    // Implement member variable
    float some_class::example = 3.14159;
    // Implement member function
    void some_class::DoStuff() const
    {
         //....
    }
}

有一个特殊的例外允许在头文件中初始化静态const积分成员,因为它允许编译器将它们视为编译时常量。也就是说,您可以使用它们来定义类定义中的数组大小或其他类似位。

答案 1 :(得分:4)

  

为什么不能在类定义中初始化其他静态数据成员?有没有具体的原因被禁止?

通常,所有静态对象都需要在一个单独的翻译单元中定义,以便它们具有明确定义的地址。作为一个特殊的例外,静态,常量,非易失性类成员不需要定义如果不需要它们的地址,并且它们具有足够简单的类型,它们的值可以被编译替换时间常数。

历史上,“足够简单”被定义为整数或枚举类型; C ++ 11将其扩展为包含具有constexpr说明符的任何文字类型。

  

如果数据成员特定于该类,为什么它们在全局命名空间范围内声明,而不是与其类相关的某些范围?

它们未在全局命名空间范围内声明。它们在课堂上被宣布并作为范围。

如果你的意思是,为什么在类定义之外定义,那是因为在整个程序中必须只有一个静态成员的定义;但必须在使用它的每个翻译单元中定义该类。

答案 2 :(得分:2)

  

为什么不能在类中初始化其他静态数据成员   定义?有没有具体的原因被禁止?

静态数据成员在很多方面(特别是从编译器的角度来看)类似于具有外部链接的命名空间范围数据对象。

静态数据成员的声明只是一个声明,而不是一个定义。它类似于全局对象的extern声明,必须包含在可以使用该对象的任何翻译单元中。

定义必须恰好出现在一个翻译单元中,这是初始化表达式所属的位置。除非表达式满足常量表达式的严格标准,否则它的值很可能取决于它所调用的时间和上下文。在多个转换单元中出现这样的初始化表达式会使初始化的执行上下文和时间最终导致初始值不明确。

对于某些类型的常量静态成员(然后可以用于初始化枚举或指定数组维度等),类范围的编译时常量被认为是足够有价值的。对于常数表达式,在不同的翻译单元中意外地产生不同的初始化值至少更加困难。这个概念在C ++ 11中扩展为constexpr成员。

  

如果数据成员特定于该类,为什么要声明它们   在全局命名空间范围内,而不是与其相关的某些范围   类?

声明在类范围内。非定义声明实际上在类定义中,定义出现在命名空间范围内,就像类成员的任何其他类外定义一样。成员名称由类名限定,因此它清楚地表示为类的成员,初始化表达式实际上被认为是在类的范围内(至少在C ++ 11中;我没有C + +98/03标准可在此处获得。)

答案 3 :(得分:1)

你必须以相反的方式看待它。基本上,必须在源文件中的类定义之外定义和初始化静态数据成员。 static const int有一个例外,因为它避免了用于定义成员数组大小的各种丑陋的变通方法。

答案 4 :(得分:-1)

每次实例化类时都会重新初始化它们。每次创建Foo类型的新对象时,所有Foos的静态变量都将重置为其初始值,这可能不是您想要的。因此,如果要对对象使用静态变量,它们要么a)不能更改它们的值,这意味着将它们重新初始化为相同的值是安全的,或者b)只能在初始化函数的上下文之外进行更改。