2010-03-03 37 views
6

我在很久没有回到C++了,而且我对于我对相当熟知的静态初始化问题的理解有点磕磕绊绊。C++静态常量和初始化(有没有一个失败)

比方说,我有一个简单的类Vector2下面给出(请注意,我知道,X和Y应该是私人的getter和setter方法,这些都只是为了简洁,省略):

class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {}; 
    float x,y; 
} 

现在,如果我想指定一个静态const成员来表示一个Vector2,其中x和y设置为1,我不确定如何继续 - 静态常量成员是否会陷入静态初始化问题或将它们设置为常量均值的行为他们都还好?我有以下几种可能酝酿:

可能性1:

// .h 
class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {} 
    static const Vector2 ONE; 
    float x,y; 
}; 

// .cpp 
const Vector2 Vector2::ONE = Vector2(1.f, 1.f); 

可能性2:

// .h 
class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {} 
    static const Vector2& getOne(); 
    float x,y; 
private: 
    static const Vector2 ONE; 
}; 

// .cpp 
const Vector2 Vector2::ONE = Vector2(1.f, 1.f); 

static const Vector2& Vector2::getOne() { 
    return ONE; 
} 

可能性3:

// .h 
class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {} 
    static const Vector2& getOne(); 
    float x,y; 
}; 

// .cpp 
const Vector2& Vector2::getOne() { 
    static Vector2 one(1.f,1.f); 
    return one; 
} 

现在,我的首选方式来写这个就像在可能性2中一样,只是因为它对我来说是一种更舒适的语法。但是,如果我从另一个类的另一个静态方法调用getOne()方法,我会冒险崩溃和燃烧?正如我所说,这是因为我使用的是静态常量而不是普通静态,因此我提出了这个问题,因为我在纯静态类成员问题上发现了很多,但对常量静态问题没有任何帮助。

我怀疑我没有得到任何东西,因为我使用的是静态常量,并且需要与Possibility 3一起才能安全,但我只是想问一下,如果有人能为我解释这一点。

我意识到我可能会打开自己的链接指向我正在问什么,但我看过,发现之前没有找到。

任何帮助将不胜感激。

+0

从你的问题来看,这并不完全清楚,但如果你试图从另一个静态初始化方法*直接访问静态成员,那么你只有**倾向于失败。在调用main()之后,从另一个函数访问它是否为static – sbk

+0

感谢您的评论sbk。我的问题应该更清楚一些。我的目的是为了确保静态成员可以被其他类中的静态初始化器在另一个cpp文件中使用。虽然我的其他类目前都没有专门使用Vector2 :: getOne()来初始化另一个静态成员,但确实存在一些静态初始化器的链。我选择Vector2作为一个简单的例子,所以我可以从这里的评论中理解在整个项目中推出的最佳模式。非常感谢评论。 –

回答

10

所有这些,除了可能性3,都受到静态初始化顺序失败的影响。这是因为你的班级不是POD。在C++ 0x中,这个问题可以通过标记构造函数constexpr来解决,但在C++ 03中没有这样的解决方案。

您可以删除构造函数来解决C++ 03的问题,并使用

const Vector2 Vector2::ONE = { 1.f, 1.f }; 

这是初始化POD初始化,并在列表中的所有初始化是常量表达式(为静态的目的初始化)。它们的初始化发生在任何代码运行之前,可能在初始化之前访问它。

3.6.2

具有静态存储的持续时间(3.7.1)的对象应是零初始化(8.5)的任何其它初始化发生之前。使用常量表达式进行零初始化和初始化统称为静态初始化;其他所有初始化都是动态初始化在任何动态初始化发生之前,初始化具有用常量表达式(5.19)初始化的静态存储持续时间的POD类型对象(3.9)。

8.5.1/14

当与静态存储持续时间的聚集体用括号括起来的初始化列表初始化,如果所有的成员初始化表达式是常量表达式,并且集合体是一个POD类型,初始化应在初始化(3.6.2)的静态阶段完成;否则,在静态阶段或初始化的动态阶段期间,具有常量表达式的成员的初始化是不确定的。

+0

感谢您的评论Johannes。我感谢您花时间回答并确认我的3种可能性,第三是唯一安全的使用方法。我没有想过要走下POD初始化路径,所以这是非常丰富的。虽然我有更复杂的类,POD方法不起作用(因为不是所有的类都可以当作POD),但仍然非常方便,因为我可以确定地看到我可以使用它的地方。非常感谢 –

+0

我刚刚意识到我从未接受过您的答案。对不起,但现在纠正,近一年后 –

1

请注意,可能性3不是线程安全的。

例如参见“C++范围的静态初始化不是线程安全的,故意的!”在http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx

+1

请注意,这个答案不再是正确的:线程安全现在[由标准施加](http://stackoverflow.com/questions/8102125/is-local-静态变量初始化线程安全的,在-C11) – Arnaud