I am returning to C++ after a long absence and I am stumbling a little over my understanding of the fairly well known static initialization problem.
Let's say I have a simple class Vector2 as given below (note that I am aware that x and y should be private with getters and setters, these have just been omitted for brevity):
class Vector2 {
public:
Vector2(float x, float y) :x(x), y(y) {};
float x,y;
}
Now, if I want to specify a static const member to represent a Vector2 with x and y set to 1, I am unsure on how to proceed - will static const members fall foul of the static initialization problem or will the act of making them const mean they are ok? I am toying with the following possibilities:
Possibility 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);
Possibility 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;
}
开发者_如何学CPossibility 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;
}
Now, my preferred way to write this would be as in possibility 2, just because it is a more comfortable syntax for me. However, if I call the getOne() method from another static method in another class am I going to risk crashing and burning? As I say, it is because I am using a static const rather than a plain static that I am asking this question as I have found much on plain static class member issues, but nothing on const static issues.
I suspect that I gain nothing by the fact that I am using static const and will need to go with Possibility 3 to be safe, but I just want to ask in case someone can shed some light on this for me.
I realise I am probably opening myself up to a slew of links pointing to exactly what I am asking, but I have looked and not found before posting this.
Any help will be gratefully appreciated.
All of them, except possibility 3
, suffer from the static initialization order fiasco. This is because your class is not a POD. In C++0x, this problem can be solved by marking the constructor constexpr
, but in C++03 there is no such solution.
You can remove the constructor to solve the problem in C++03, and initialize using
const Vector2 Vector2::ONE = { 1.f, 1.f };
This is initializing a POD, and all initializers in the list are constant expression (for the purpose of static initialization). The intialization of them happen before any code is run that might access it before being initialized.
3.6.2
:
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.
8.5.1/14
:
When an aggregate with static storage duration is initialized with a brace-enclosed initializer-list, if all the member initializer expressions are constant expressions, and the aggregate is a POD type, the initialization shall be done during the static phase of initialization (3.6.2); otherwise, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization.
Please note that possibility 3 is not thread safe.
See for example "C++ scoped static initialization is not thread-safe, on purpose!" at http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx
精彩评论