【问题标题】:Destruction of local static objects破坏局部静态对象
【发布时间】:2020-10-27 01:25:59
【问题描述】:

考虑这样的情况:

#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();
}

在编译器的实现中,我在代码中研究了这些结果,该代码通过调用_atexit 设置S::~S(),即在main() 退出后的某个时刻本地静态对象将不复存在。

如果全局静态/外部作用域中对象的析构函数调用具有函数局部静态作用域的函数,根据定义,该函数在第一次执行进入此作用域时被构造,并且来自析构函数的调用是第一次?这也可能是对象函数局部静态作用域的析构函数在函数局部静态作用域中构造另一个对象的情况。

这可能是代码库依赖于 Scott Meyers 的单例实现的多个实例的情况,其中对象实例是函数局部静态变量。我不确定如果这样的单例必须在这个执行阶段访问标准流会发生什么,是否确定它们在atexit 处理程序之后何时停止运行。

【问题讨论】:

  • 我认为你应该改写你的问题更准确。我想你是在问,如果全局静态/外部范围内的对象的析构函数调用具有函数局部静态范围的函数,根据定义,该函数在第一次执行进入此范围并从析构函数是第一次。无论这是未定义的行为,还是存在可以采用任何一种方式的声明顺序依赖项。这实际上是一个非常有趣的语言律师问题。
  • @SamVarshavchik 我可以复制你的措辞吗?嘿,英语不是我的母语,现在是凌晨 4 点
  • 做我的客人,不客气。
  • 在什么情况下您希望对象生命周期在 main() 返回之后开始?在这些情况中,有多少会存在替代实现或设计选择,其中该对象的至少部分生命周期(例如其构造)发生在main() 返回之前?在任何情况下,标准 I/O 流(cout 等)都由标准保证可在静态对象的构造函数和析构函数中访问。
  • 我在标准中没有看到任何直接解决此问题的内容。使用已销毁的静态对象调用函数是未定义的行为。我怀疑第一次调用带有静态变量的函数要么是好的一次(由于静态对象销毁规则,它会在完成当前对象析构函数后立即被销毁)然后是 UB,或者可能总是只是 UB。跨度>

标签: c++ singleton


【解决方案1】:

这是一个已知解决方案的已知问题,不幸的是它相当复杂。我知道的最好的方法是来自 Modern C++ Design 的Phoenix Singleton。简而言之,您可以依赖非类单例的值,并且您可以重用s的内存在同一位置重新创建S对象.

另一种方法是将一些实用程序单例替换为由其余单例共同拥有的对象,即通过shared_ptr。这将在最后一个单例超出范围后被删除。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-20
    相关资源
    最近更新 更多