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