【问题标题】:C++ static const and initialization (is there a fiasco)C++ 静态常量和初始化(有没有惨败)
【发布时间】:2011-01-23 08:24:49
【问题描述】:

我在久违了 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,我不确定如何进行 - 静态 const 成员会遇到静态初始化问题还是会发生以下行为让他们成为 const 意味着他们还好?我正在玩弄以下可能性:

可能性一:

// .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 静态问题上却一无所获。

我怀疑我使用 static const 并需要使用可能性 3 以确保安全这一事实没有任何收获,但我只是想问一下,以防有人可以为我阐明这一点。

我意识到我可能正在向大量指向我所要求的链接的链接敞开大门,但在发布之前我已经查看并没有找到。

任何帮助将不胜感激。

【问题讨论】:

  • 您的问题并不完全清楚,但如果您尝试从 另一个静态初始化程序 直接与否。在调用 main() 之后从另一个静态函数访问它是可以的
  • 感谢您的评论 sbk。我的问题应该更清楚一点。我的目的是确保在单独的 cpp 文件中的另一个类中的静态初始化程序中使用静态成员是安全的。尽管我的其他类目前没有一个专门使用 Vector2::getOne() 来初始化另一个静态成员,但我确实遇到了存在静态初始化器链的情况。我选择 Vector2 作为一个简单的例子,这样我就可以从这里的 cmets 中了解在我的整个项目中推广的最佳模式。非常感谢您的评论。

标签: c++ static initialization constants


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

除了可能的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);否则,未指定具有常量表达式的成员的初始化是在初始化的静态阶段还是在动态阶段进行。

【讨论】:

  • 感谢您的评论约翰内斯。感谢您花时间回答并确认我的 3 种可能性,第 3 种是唯一可以安全使用的。我没有想过沿着 POD 初始化路径走下去,所以这是非常有用的。尽管我有更复杂的类,其中 POD 方法不起作用(因为并非我的所有类都可以被视为 POD),但知道它仍然非常方便,因为我肯定可以看到我可以使用它的地方。非常感谢
  • 我才意识到我从来没有接受过你的回答。对此感到抱歉,但现在已纠正,将近一年后
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-15
  • 1970-01-01
相关资源
最近更新 更多