2010-09-24 22 views
2

说我有定义的变量是这样的(C++):在静态反初始化过程中,静态分配的内存可能无效吗?

static const char str[] = "Here is some string data"; 

而且我有一个静态分配的类实例,它引用该阵列中的析构函数,可以在此出问题?例如。某种程度上,变量会变得无效吗?

class A { 
~A() { 
    cout << str << endl; 
} 
}; 

static A a; 

我的假设是它不会出错,但我可以在任何地方清楚地发现它。我很想知道这一点。我的假设是,我们无法预测静态分配对象的析构函数被调用的顺序,但是在流程被拆除之前,数据本身永远不会被释放。含义指针指向POD应该是安全的,但不是对象实例。

含义例如这样的:

static const QString str = "Here is some string data"; 

static const std::string str = "Here is some string data"; 

无法安全地在一个的析构函数中使用,因为它们都在堆上分配的字符串数据,这可能是由析构函数之前一个被释放调用的析构函数。

我的假设是否正确,并且C++标准中是否有任何章节解释了这个问题,或者是某些可以验证这一点的其他权威机构的链接?

+1

有在Alexandrescu的书(现代C++设计)与静态对象的初始化所带来的问题和潜在的解决方案的大讨论 – 2010-09-24 09:43:06

+0

是的,你说得对,但什么是我的保证值得吗......? :-)。我也喜欢Alexandrecu在Singleton实现中的处理。那么,你基本上是在寻找标准的一​​部分来保证字符串文字在范围内,而静态对象析构函数被执行? Chubsdad在哪里? ;-) – 2010-09-24 09:50:41

回答

2

好吧我试着自己阅读C++标准来找到一些答案。我从我得到的答案中看到,构建一个对象和分配对象之间存在着很多混淆。

从标准:

3.6.2 之前任何非本地对象

对象具有静态存储持续时间 (3.7.1)须零初始化 (8.5)的初始化其他初始化 发生。具有静态存储持续时间的类型的存储持续时间和POD 类型的对象的参考可以使用常数 表达式(5.19)来初始化: 。这称为 不断的初始化。 零初始化和常量 初始化称为静态初始化; 初始化;其他所有 初始化都是动态的 初始化。静态初始化 应在任何动态初始化发生之前执行。一个对象的动态初始化是 有序或无序。

我对此的口译是一个为const char []将始终保证运行的任何构造之前已设置。

3.6.3终止 析构函数(12.4),用于静态存储持续时间的初始化的对象(在块范围或在命名空间范围中声明)是 称为从主和作为调用的std的结果返回的结果: :出口(18.3)。这些对象在其构造函数完成或动态初始化完成的逆向顺序中被销毁。如果一个对象是静态初始化的 ,则该对象按照与对象被动态初始化相同的顺序销毁。

从我可以从这个POD类型的常量表达式将读取任何对象类型前初始化和任何对象类型后销毁。意思是没有代码可以运行,可以在无效的时候访问它们。

这应该解释为什么谷歌的C++代码标准说你应该只使用POD types with constant expressions.

因此,我们只允许静态变量包含POD数据。此规则完全禁止使用vector(使用C数组)或string(使用const char [])。

2

调用自动,全局和静态对象的析构函数的顺序总是定义良好:它与调用构造函数的顺序相反。因此,如果对象A引用BBA构造一个对象,你可以肯定的是BA后销毁。这给我们留下了构造函数顺序的问题。

这是我记得:构建

  1. 全局和静态对象main()被调用之前。
  2. 类本地静力学是在他们班级的任何对象之前构建的。
  3. 函数局部静态是在第一次达到它们的作用域时构造的。
  4. 在同一翻译单元内的全局和静态对象按其定义的顺序构建。 (这意味着包含标题的顺序可能会对此产生影响!)
  5. 跨转换单元的全局和静态对象的构建顺序未定义。

我对这些有些朦胧,所以请纠正我,如果你认为这是不对的。

+0

给定TU中的静态在来自TU的第一个函数被调用之前被初始化。类静态类在它们类的对象之前未被初始化,而是取决于它们初始化的位置(即它所在的TU以及它在之前或之后的静态存储持续时间的其他对象)。当来自TU A的静态调用来自TU B的函数时,该问题的情况出现了,该TU B引用尚未初始化的TU A的静态。 – 2010-09-24 13:25:47

+0

@Anthony:我觉得我不确定。 IIUC,你说我的规则#2是错误的,#4需要增加(在TU的任何功能被调用之前,TU的静态都被初始化了)我是否明白这一点?此外,什么是这个功能呢?我想不是这些静力学的影子?那些功能叫做什么功能呢? – sbi 2010-09-24 18:47:16

+0

你的规则#2是错误的。你的规则#4是正确的。规则#5需要补充:如果在调用'main'时没有初始化来自TU的对象,则必须在使用该TU中定义的任何对象之前对其进行初始化,否则将调用该TU中定义的任何函数。然而,在调用'main'之前的初始化之后,你不能依赖其他正在初始化的TU。 – 2010-09-24 21:42:17

0

如果我没有记错,全局对象初始化没有在标准(或定义不明确)中定义,使全局对象很难相互引用。

如果您想确定初始化顺序,请在其中使用静态对象的全局函数,并返回它。现在您已经确保静态对象将在第一次函数调用时被初始化。

销毁将在应用程序结束时发生,一旦出现main()。

+0

这里我主要关心的是销毁阶段。静态分配的数据是否会被释放? (我知道析构函数会运行,但这并不意味着数据必然会被释放)。 – 2010-09-29 10:26:19

+0

它们被解除分配,但是在main()之后并且以未定义的顺序。确定订单的唯一方法是使用创建和销毁方法创建单身人士。 – Klaim 2010-09-29 11:01:00