文件作用域的静态对象在所属翻译单元的静态初始化发生之前是否被零初始化?

3

在文件maybe_use_foo.cpp中:

namespace {
    class Foo { /* ... */ };
    Foo* const the_foo = new Foo;
}

void funtion_callable_from_another_tu_during_process_wide_initialization() {
    // If we haven't yet run static initialization for this TU,
    // but some other static initializer called us, ignore the request.
    if (!the_foo)
      return;

    // OK, static initializers for this TU have run, foo exists, use it.
    the_foo->doSomething();
}

所以,不管上述方法是否可取,它总是有效的吗?在我看来,它假设静态变量在TU的静态初始化之前被零初始化。C++标准(C++03?C++11?)是否保证了这一点?

另一种提问方式是询问哪个值序列,当解释为Foo*时,保存在“the_foo”的存储器中。它肯定是{NULL/nullptr,new Foo}吗?还是{未定义,new Foo},甚至是其他东西?

请不要建议其他组织方式:我不是在寻求如何更好地完成此任务的建议,而是在寻求对该技术合法性的深入理解。

3个回答

4

C++11

[basic.start.init]/2

具有静态存储期(3.7.1)或线程存储期(3.7.2)的变量在进行任何其他初始化之前必须进行零初始化(8.5)。

[...]

零初始化和常量初始化一起称为静态初始化;所有其他初始化都是动态初始化。 静态初始化应该在进行任何动态初始化之前执行。

也就是说,是的,变量被零初始化了,但不是在静态初始化之前进行零初始化(而是作为静态初始化的一部分进行的)。


OP中的函数只会在动态初始化期间调用,因为它不会在零初始化期间被调用,并且必须是一个constexpr函数才能成为常量初始化的一部分。


等等,Loki loki(5); 这个呢?在动态初始化之前是否进行了零初始化。构造函数不是 constexpr。因此不符合 constant-initialization 的要求。 - Martin York
@LokiAstari 从语义上讲,即使是常量初始化的非局部变量也会首先进行零初始化。是的,类类型的非局部变量也将被零初始化,但这意味着(对于非联合类型)每个成员(和填充)都进行递归零初始化。本质上,.bss。然而,在某些情况下,常量甚至动态初始化也可以在编译时应用(“如果不破坏任何东西”),这将把它放在.data段中。 - dyp
好的。现在看到它了。零初始化不需要构造函数。它只是将内存清零。 - Martin York

4
是的,C++03标准在[basic.start.init]中解释道:
静态存储期对象(3.7.1)必须在进行任何其他初始化之前进行零初始化(8.5)。
8.5.1解释如下:
要对类型为T的对象进行零初始化意味着:
- 如果T是标量类型(3.9),则将对象设置为转换为T的0(零)值; - [...]

0
你的代码可能会导致错误。链接器通常不会深入理解不同模块中的初始化部分。最好通过静态函数来提供变量。
namespace {
    Foo* the_foo() {
        static Foo* g_ = new Foo;
        return g_;
    }
}

正如我在问题中提到的,我并不打算重新组织这段代码(如果有的话)。我试图理解已经按照上述模式编写的现有代码是否具有定义行为。 - acm
@acm:是的,抱歉这个评论没有什么用。 - Wolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接