我很久以后就回到了C ++,而且我对这个众所周知的静态初始化问题的理解有点磕磕绊绊。
假设我有一个简单的类Vector2,如下所示(请注意,我知道x和y应该是getter和setter的私有,为简洁起见,这些只是省略了):
class Vector2 {
public:
Vector2(float x, float y) :x(x), y(y) {};
float x,y;
}
现在,如果我想指定一个静态const成员来表示一个x和y设置为1的Vector2,我不确定如何继续 - 静态const成员会违反静态初始化问题或者是让他们意味着他们还好吗?我正在玩弄以下各种可能性:
可能性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()方法,我将冒险崩溃和刻录?正如我所说,这是因为我使用静态const而不是普通的静态我问这个问题,因为我在普通的静态类成员问题上发现了很多,但在静态问题上却没有。
我怀疑我没有得到任何东西,因为我使用静态const并且需要使用Possibility 3才能安全,但我只是想问一下,以防有人可以为我阐明这一点。
我意识到我可能会打开一大堆链接,指向我正在询问的内容,但在发布此内容之前我已经看过并且没有找到。
感谢任何帮助。
答案 0 :(得分: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);否则,未指定具有常量表达式的成员的初始化是在静态阶段还是在初始化的动态阶段期间发生。
答案 1 :(得分:1)
请注意,可能性3不是线程安全的。
例如,参见“C ++范围的静态初始化不是故意保护线程的!”在http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx