【问题标题】:Are static class members guaranteed to be initialized before `main` is called?静态类成员是否保证在调用 main 之前被初始化?
【发布时间】:2013-04-24 09:43:31
【问题描述】:

是否可以保证在调用main 之前初始化静态类成员?

【问题讨论】:

  • AFAIK,没有。它们在第一次使用/访问时被初始化。在调用 main 之前,只会初始化全局静态变量。
  • @Downvoter:请用我的问题内容解释您的问题。谢谢。
  • 是的,自我回答问题(尤其是做得好时)在这里完全可以接受。
  • "-1 白痴,回答他自己的问题。下次你发现一些有趣的知识时,把它留给自己,好吗?没有人希望你与世界分享这些知识。我宁愿保持无知,也不愿看到你获得一些想象中的互联网美元积分。”
  • @thecoshman:错误,静态类成员的行为方式与全局静态完全相同,可能在main之前初始化也可能不初始化(但需要在第一次使用之前初始化)。您的评论适用于 function local 静态变量,不适用于 class static members

标签: c++ language-lawyer c++03


【解决方案1】:

我认为没有

[C++03: 3.6.2/3]: 命名空间范围对象的动态初始化 (8.5, 9.4, 12.1, 12.6.1) 是否在第一条语句之前完成由实现定义main 的。 如果初始化推迟到 main 的第一个语句之后的某个时间点,它应该发生在第一次使用与对象相同的翻译单元中定义的任何函数或对象之前被初始化。


嗯,真的

好吧,可以说,“在命名空间范围内定义”与“命名空间范围的对象”并不完全相同:

[C++03: 9.4.2/2]: 在其类定义中声明 static 数据成员不是定义,并且可能是除 cv 限定的 void 之外的不完整类型。 static 数据成员的定义应出现在包含该成员的类定义的命名空间范围内。在命名空间范围的定义中,static 数据成员的名称应使用:: 运算符由其类名限定。 初始化器 静态数据成员定义中的表达式在其类(3.3.6)的范围内。

但是,initializer 在类的范围内;没有提到 static 成员本身除了命名空间范围之外还有其他任何东西(除非我们在脑海中将词 “lexically” 注入到任何地方)。

这个令人愉悦的段落:

[C++03: 9.4.2/7]: 静态数据成员的初始化和销毁​​与非本地对象(3.6.2、3.6.3)完全相同。

然而,不幸的是,关于“非本地对象”,main 的排序和静态初始化的唯一进一步定义是前面提到的[C++03: 3.6.2/3]


然后呢?

我相信 C++11 中的新措辞清楚地表明了这条原本可能模棱两可的规则的意图,它解决了所有问题:

[C++11: 9.4.2/6]: 静态数据成员的初始化和销毁​​与非局部变量(3.6.2、3.6.3)完全相同。

[C++11: 3.6.2/4]: 是否在main的第一条语句之前进行静态存储时长的非局部变量的动态初始化是实现定义的。 [..]

【讨论】:

  • 你的提问时间和回答时间怎么一模一样?两者都显示 12:55:04 在我的? :-)
  • 我在我的 C++03 标准 9.4.2/7 中找到 [c++11: 9.4.2/6]... 新措辞??
  • @Koushik:这是一个自我回答的问题,您可以通过一次提交来发布问题和答案。
  • @DyP:啊!你说得对!我完全不知何故错过了!如果您想继续自己发布它,那么这就是通往明确正确答案的道路......如果没有,我会编辑我的,但选择是你的。 [编辑:实际上,如果没有 C++11 的 3.6.2/4,C++03 可能仍然在这里模棱两可]
  • 还有一个使用静态成员函数(带有局部静态变量)的“解决方法”,它在初始化点和初始化顺序上都没有问题。
【解决方案2】:

C++03:总之,不保证

C++11:不保证,请参阅 Lightness 的回答。

我对 C++03 语句的解释/分析:


术语:[basic.start.init]/1

零初始化和用常量表达式初始化统称为静态初始化;所有其他初始化都是动态初始化。


非本地对象的初始化顺序:

具有静态存储持续时间 (3.7.1) 的对象应在任何其他初始化发生之前进行零初始化 (8.5)。

但它没有提及“任何其他初始化”何时发生,即不能保证它会在 main 的第一个语句之前,即使是零初始化

具有使用常量表达式 (5.19) 初始化的静态存储持续时间的 POD 类型 (3.9) 的对象应在任何动态初始化发生之前进行初始化。

但同样,不能保证。


动态初始化

[basic.start.init]/3

命名空间范围对象的动态初始化(8.5、9.4、12.1、12.6.1)是否在main的第一条语句之前完成是实现定义的。如果初始化延迟到 main 的第一个语句之后的某个时间点,它应该发生在第一次使用与要初始化的对象在同一翻译单元中定义的任何函数或对象之前。

但是什么是“命名空间范围的对象”?我在标准中没有找到任何明确的定义。 scope 实际上是 name 的属性,而不是 object 的属性。因此,我们可以将其理解为“在命名空间范围内定义的对象”或“由命名空间范围的名称引入的对象”。注意动态初始化后的参考“9.4”。它指的是“静态成员”,只能表示静态数据成员。所以我会说它的意思是“在命名空间范围内定义的对象”,因为静态数据成员是在命名空间范围内定义的:

[class.static.data]/2

静态数据成员的定义应出现在包含该成员的类定义的命名空间范围内。

即使您不同意这种解释,仍然有 [basic.start.init]/1

在同一个翻译的命名空间范围内定义的具有静态存储持续时间的对象 单元和动态初始化应按照其定义在翻译单元中出现的顺序进行初始化。

这显然适用于静态数据成员,这意味着如果在静态数据成员的定义之前存在这样的对象,则它们的初始化方式不能不同于命名空间范围的名称引入的对象。也就是说,如果根本无法保证静态数据成员的动态初始化,则任何前面的由命名空间范围的名称引入的对象的保证都将适用——它们是:无(它不必须在main的第一条语句之前初始化)。

如果在静态数据成员的定义之前没有这样的对象并且您不同意解释 - 根本无法保证静态数据成员的动态初始化。


结论

因此,我们只能保证动态初始化在某个时间(在任何使用之前)发生,以及不能消除带有副作用的初始化的异常。尽管如此,我们仍不能保证在main 的第一条语句之前执行非本地对象的任何类型的初始化


注意:有一些解决方法,例如:

#include <iostream>

struct my_class
{
    static int& my_var()
    {
        static int i = 42;
        return i;
    }
};

int j = ++my_class::my_var();
int k = ++my_class::my_var();

int main()
{
    std::cout << j << " : " << k << std::endl;
}

【讨论】:

    猜你喜欢
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 2018-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多