为什么非const,非int/enum静态数据成员必须在定义之外进行初始化?
我知道只有静态,常量和int/enum(pre C++ 11)的数据成员才能在类声明中初始化。 “所有其他静态数据成员都必须在全局命名空间范围内定义(即在类定义的主体之外),并且只能在这些定义中初始化”。为什么非const,非int/enum静态数据成员必须在定义之外进行初始化?
为什么其他静态数据成员不能在类定义中初始化?这是被禁止的具体原因吗?
如果数据成员是特定于类的,为什么它们在全局命名空间范围内声明,而不是某些与其类相关的范围?
为什么其他静态数据成员不能在类定义中初始化?这是被禁止的具体原因吗?
很可能是因为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
{
//....
}
}
有一个特定的异常,让静态常量组成成员的初始化因为它允许编译器将它们视为编译时常量。也就是说,您可以使用它们来定义类定义中的数组或其他类似位的大小。
我认为你的最后一段是我第一个问题的答案?另外,你是说“全局命名空间范围”实际上是指所讨论的类/结构的“全局命名空间范围”?这不是真正的GLOBAL命名空间....? – user997112 2013-03-01 21:58:02
@user:是的。最后的注释“(即在类定义的主体之外)”正在解决真正意义上的问题 - 只需要在某个地方定义一个定义即可。 – 2013-03-01 22:00:11
每次该类被实例化时,它们都会被重新初始化。每次创建一个Foo类型的新对象时,所有Foos的静态变量都将重置为其初始值,这可能不是您想要的。因此,如果你想在你的对象中使用静态变量,它们要么a)不能改变它们的值,这意味着将它们重新初始化为相同的值是安全的,或者b)只能在初始化函数的上下文之外改变。
这没有任何意义。为什么当每个实例被实例化时都会发生静态成员的初始化? – 2013-03-01 21:49:25
为什么其他静态数据成员不能在类定义中初始化?这是被禁止的具体原因吗?
通常,所有静态对象都需要在一个单独的翻译单元中定义,以便它们具有定义明确的地址。作为一个特殊的例外,静态,常量,非易失类成员不需要定义如果其地址不是必需的,并且它们具有足够简单的类型,以便它们的值可以被编译时常量替换。
从历史上看,“足够简单”被定义为整数或枚举类型; C++ 11将其扩展为包含具有constexpr
说明符的任何文字类型。
如果数据成员是特定于类的,为什么它们在全局命名空间范围内声明,而不是某些与其类相关的范围?
它们没有在全局命名空间范围内声明。他们在课堂上被宣布和确定范围。
如果你的意思是,他们为什么在类定义之外定义了,这是因为在整个程序中必须只有一个静态成员的定义;但该类必须在使用它的每个翻译单元中定义。
+1。当然,他们想出了一些内联函数(你可以把它的地址),大概他们可以使用相同的机制。 – 2013-03-01 22:01:02
@BillyONeal:确实可以。但他们没有。 – 2013-03-01 22:01:51
为什么其他静态数据成员不能在类 定义中初始化?这是被禁止的具体原因吗?
静态数据成员在很多方面(特别是从编译器的角度来看)类似于具有外部链接的名称空间范围数据对象。
静态数据成员的声明只是一个声明,而不是一个定义。它类似于一个全局对象的extern
声明,并且必须包含在可能使用该对象的任何翻译单元中。
定义必须出现在一个翻译单元中,并且这是初始化表达式所属的位置。除非一个表达符合常量表达式的严格标准,否则其值可能取决于它所称的时间和环境。在多个翻译单元中出现这种初始化表达式会使初始化的执行上下文和时间以及最终的初始值不明确。
一个类范围的编译时常量被认为足够有价值,可以对某些类型的常量静态成员(然后可以用来初始化枚举或指定数组维度等)进行例外。使用常量表达式,在不同的翻译单元中偶然会产生不同的初始值。这个概念在C++ 11中用constexpr
成员进行了扩展。
如果数据成员是具体的类,为什么他们宣布 在全局命名空间范围,而不是某些范围与他们 类?
声明在类范围内。非定义声明实际上是在类定义中,并且定义显示在名称空间范围内,就像类成员的任何其他类外定义一样。成员名称由类名限定,所以它明确表示为类的成员,并且初始化表达式实际上被认为是在该类的范围内(至少在C++ 11中;我没有C + +98/03标准在这里可用)。
“翻译单元”的含义是“class.h”+“class.cpp”? – user997112 2013-03-01 22:05:44
“翻译单元”是编译器编译的一个单元。通常这是一个单独的“x.cpp”文件。头文件通过'#include'指令(递归地)包含在翻译单元中。所以“class.h”的内容是包含文件(直接或间接)的每个翻译单元的一部分。 “class.cpp”的内容仅在一个翻译单元(通常)中出现。 – JoergB 2013-03-01 22:11:11
@user:N3485 2.1 [lex.seperate]/1:本标准中的程序文本保存在称为源文件的单元中。源文件 连同所有头文件(17.6.1.2)和包含源文件(16.2)的预处理指令 #include,少于任何有条件包含(16.1)预处理指令跳过的源代码行,被称为 翻译单位。 [注意:一个C++程序不需要全部同时翻译。 - 结束] – 2013-03-01 22:11:25
你必须反过来看看它。基本上,静态数据成员必须在类定义之外的源文件中定义和初始化。 static const int
有个例外,因为它避免了定义成员数组大小的各种丑陋的变通方法。
“全局命名空间范围”具有误导性 - 定义发生在实际类的名称空间范围内;不是全局命名空间的范围。 (正如我在答案中所示) – 2013-03-01 21:47:53