我遇到了一些静态初始化顺序混乱的问题。我有一个类,在我的程序进入主程序之前不应该实例化/构造它。这是因为当程序进入主程序时,所有的静态初始化都已经完成了。显然,一个简单的答案就是不要将该类型的对象实例化/构造/初始化为静态变量,但我想进行检查,以防它意外发生并且有一个我无法发现的错误。

我考虑过设置一个标志 bHasEnteredMain,将其设置为 main 中的第一项,但这没有用,因为没有初始化顺序,因此该标志在该内存中具有与 true 等同的垃圾,所以它毫无用处。有办法吗?C++ 标准或编译器中是否有内置标志或类似的东西可以用作检查?

8

  • “这是因为当程序进入主程序时,所有静态初始化都已完成。” ——不一定,尽管猜测(没有看到您的代码)它可能足够接近您的目的。


    – 

  • @JaMiT 你说的不一定是什么意思?这不是保证吗?


    – 

  • 找到了:(但是,我没有找到我记得的答案,很好地解释了静态变量的静态初始化和静态变量的动态初始化之间的区别。)


    – 

  • @JaMiT 来自 CPPReference:“所有具有静态存储持续时间的非局部变量都在程序启动时初始化,在主函数开始执行之前(除非延迟,见下文)。”然后下面的部分指的是“延迟动态初始化”。所以在某些情况下,动态初始化可能会在主函数进入后发生?这太难了。我只是想让这有意义。


    – 

  • “这太难了。我只想让它有意义” ——尝试解决具体问题,而不是泛泛而谈“在我的程序进入主程序之前”。研究为什么存在这种限制。尝试检测限制的原因,而不是开始的原因main


    – 


最佳答案
2

您可以拥有一个设置为的全局布尔值,然后在主函数中将false其更改为。true

在许多平台上,全局简单对象在 C++ 运行时开始初始化非简单对象之前就已初始化。

另一个解决方案是使用 meyers 单例等价于计数器,在 main 开始时以及在不想静态构造的类的构造函数中调用它。

int count()
{
    static int counter = 0;
    counter++;
    return counter;
}

如果返回除 1 之外的任何数字,main那么它是在静态初始化期间调用的,并且存在该类的静态实例!

9

  • 现在当对象位于另一个 TU 中时会发生什么?


    – 

  • @NathanOliver 没关系,C++ 运行时尚未启动,它们在将应用程序或共享对象加载到内存中时由动态链接器初始化。


    – 


  • @NathanOliver 这可能与本例无关,因为这个全局变量专门供特定类使用,因此应在同一个 TU 中声明。它不是您打算在许多不同库中使用的通用标志。


    – 

  • 1
    @Barmar 静态初始化顺序混乱的重点在于多个 TU 中的静态对象没有定义顺序。这难道不意味着涉及多个 TU 吗?如果这一切都在一个 TU 中,那么一开始就没有问题,因为每个 TU 都有保证的总顺序。


    – 


  • @NathanOliver 然后我怀疑 OP 对未定义的静态初始化顺序感到困惑,并且认为它在 TU 中适用。


    – 

最好使用内部带有静态标志的函数(Meyers 的类似单例的构造)。根据需要,您可以这样做,例如:

#include <iostream>

//yes, the interface is somewhat clunky
bool mainWasCalled(bool mark = false)
{
    static bool wasCalled{};
    if (!wasCalled && mark) {
        wasCalled = true;
    }
    return wasCalled;
}


struct S
{
    S() { std::cout << mainWasCalled() << '\n'; }
};

static S staticS;
S globalS;

int main()
{
    mainWasCalled(true);
    S localS;
}

计数器递增main也可以工作,选择你喜欢的接口。这基于以下原则:函数内的静态对象可防止静态初始化顺序失败——它在进入函数时被初始化。函数本身有效地操纵(并返回)全局变量的状态。

唯一的问题可能是当您的全局对象尝试从多个线程中调用此函数时;您可能会main()在调用此函数之前与其他一些线程之间产生数据争用。