我知道只有静态、常量和 int/enum (pre c++11) 的数据成员才能在类声明中初始化。“所有其他静态数据成员必须在全局命名空间范围内定义(即在类定义的主体之外),并且只能在这些定义中初始化”。
为什么不能在类定义中初始化其他静态数据成员?是否有特定原因被禁止?
如果数据成员是特定于类的,为什么它们是在全局命名空间范围内声明的,而不是与其类相关的某个范围?
为什么不能在类定义中初始化其他静态数据成员?是否有特定原因被禁止?
很可能是因为 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 整数成员有一个特定的例外,因为它允许编译器将它们视为编译时常量。也就是说,您可以使用它们来定义数组的大小或类定义中的其他类似位。
为什么不能在类定义中初始化其他静态数据成员?是否有特定原因被禁止?
通常,所有静态对象都需要在一个翻译单元中进行定义,以便它们具有明确定义的地址。作为一个特殊的例外,静态、常量、非易失类成员如果不需要其地址,则不需要定义,并且它们具有足够简单的类型,可以将其值替换为编译时常量。
从历史上看,“足够简单”被定义为整数或枚举类型;C++11 将其扩展为包含任何带有constexpr
说明符的文字类型。
如果数据成员是特定于类的,为什么它们是在全局命名空间范围内声明的,而不是与其类相关的某个范围?
它们未在全局命名空间范围内声明。它们在类中声明和作用域。
如果你的意思是,为什么要在类定义之外定义它们,那是因为在整个程序中必须只有一个静态成员的定义;但是必须在使用它的每个翻译单元中定义该类。
为什么不能在类定义中初始化其他静态数据成员?是否有特定原因被禁止?
静态数据成员在许多方面(尤其是从编译器的角度来看)类似于具有外部链接的命名空间范围数据对象。
静态数据成员的声明只是一个声明,而不是定义。它类似于extern
全局对象的声明,并且必须包含在可以使用该对象的任何翻译单元中。
定义必须恰好出现在一个翻译单元中,这是初始化表达式所属的地方。除非表达式满足常量表达式的严格标准,否则它的值很可能取决于调用它的时间和上下文。在多个翻译单元中出现这样的初始化表达式会使初始化的执行上下文和时间以及最终的初始值不明确。
类范围的编译时常量被认为足够有价值,可以为某些类型的常量静态成员(然后可用于初始化枚举或指定数组维度等)进行例外处理。使用常量表达式至少更难在不同的翻译单元中意外地产生不同的初始化器值。这个概念在 C++11 中通过constexpr
成员进行了扩展。
如果数据成员是特定于类的,为什么它们是在全局命名空间范围内声明的,而不是与其类相关的某个范围?
声明在类范围内。非定义声明实际上是在类定义中,并且定义出现在命名空间范围内,就像类成员的任何其他类外定义一样。成员名由类名限定,所以明确表示为类的成员并且初始化表达式实际上被认为是在类的范围内(至少在C++11;我没有C+ +98/03 标准在这里可用)。
你必须反过来看。基本上,静态数据成员必须在源文件中的类定义之外定义和初始化。有一个例外,static const int
因为它避免了定义成员数组大小的各种丑陋的变通方法。
每次实例化类时,它们都会重新初始化。每次创建 Foo 类型的新对象时,所有 Foo 的静态变量都会重置为其初始值,这可能不是您想要的。因此,如果您想在对象中使用静态变量,它们要么 a) 无法更改其值,这意味着将它们重新初始化为相同的值是安全的,或者 b) 只能在初始化函数的上下文之外进行更改。