本地静态对象的销毁

3

考虑这样一种情况:

#include <iostream>

int foo() {
    static struct S {
        int value;
        S(int a): value(a) {}~S() {
            std::cout << "End is nigh";
        }
    }
    s(42);
    return s.value;
}

int main() {
    return foo();
}

在编译器的实现中,我调查了设置S::~S()_atexit调用的代码,即在main()退出后某个时间点上,局部静态对象将停止存在。
如果全局静态/外部范围内的对象的析构函数调用一个具有函数局部静态范围的函数,根据定义,该函数局部静态范围在第一次执行进入该范围时会被构造,并且来自析构函数的调用是第一次吗?这也可能是一个在函数局部静态范围内构造另一个对象的析构函数的情况。
这可能是一个依赖于Scott Meyers的单例模式的多个实例的代码库,其中对象实例是一个函数局部静态变量的情况。如果这样的单例必须在执行此阶段时访问标准流,我不确定是否已经确定它们在atexit处理程序之后停止工作。

2
我认为你应该重新表述你的问题,更加精确。我认为你是在问如果一个全局静态/外部作用域对象的析构函数调用了一个具有函数本地静态作用域的函数,根据定义,第一次执行进入此作用域时会构造该函数,而从析构函数中的调用就是第一次。这是否是未定义行为,或者存在声明顺序依赖性,可以任意选择。这实际上是一个非常有趣的语言法律问题。 - Sam Varshavchik
@SamVarshavchik,我可以借鉴你的措辞吗?哈哈,英语不是我的母语,现在是凌晨4点。 - Swift - Friday Pie
1
请随意,你可以使用它。 - Sam Varshavchik
1
在什么情况下,您期望对象的生命周期在 main() 返回后才开始?有多少这样的情况会有替代实现或设计选择,在其中该对象的部分生命周期(例如其构造)发生在 main() 返回之前?无论如何,标准 I/O 流(cout 等)都保证可以在静态对象的构造函数和析构函数中访问。 - Peter
@Peter 是新手开发人员根据设计模式学习并渴望使用的一整套选择。其中最简单的是Singleton,但在某些情况下被过度使用,甚至使用不当(全局初始化混乱肯定会发生)。适当的去初始化非常重要,因为其中一些对象会操作系统范围内的共享资源(共享内存?d-bus?),而在进程结束时平台不会清理它们。这是我个人同意Singleton是反模式的声明的原因。标准流被用于“调试”此顺序。 - Swift - Friday Pie
1
我在标准中没有看到直接解决这个问题的内容。调用已经销毁的静态对象的函数是未定义行为。我怀疑第一次调用带有静态变量的函数是可以的,因为它会在当前对象析构函数完成后立即被销毁,遵循静态对象销毁规则,然后就是未定义行为,或者可能总是未定义行为。 - 1201ProgramAlarm
1个回答

1
这是一个已知问题,有已知的解决方案,但不幸的是,解决方案相当复杂。我所知的最佳方法是来自现代C++设计的Phoenix Singleton。简而言之,您可以依赖于非类单例的值,并且可以重用s的内存,在同一位置重新创建S对象。
另一种方法是通过由剩余单例共同拥有的对象(即通过shared_ptr)替换一些实用程序单例。这将在最后一个单例超出范围后被删除。

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