【问题标题】:How to do static de-initialization if the destructor has side effects and the object is accessed from another static object's destructor?如果析构函数有副作用并且对象是从另一个静态对象的析构函数访问的,如何进行静态反初始化?
【发布时间】:2011-03-09 18:17:06
【问题描述】:

有一个简单且众所周知的模式来避免静态初始化失败,在section 10.13 of the C++ FAQ Lite 中进行了描述。

在这个标准模式中,有一个权衡,即构造的对象永远不会被破坏(如果析构函数没有重要的副作用,这不是问题),或者不能安全地从另一个对象访问静态对象静态对象的析构函数(参见section 10.14 of the C++ FAQ Lite)。

所以我的问题是:如果静态对象的析构函数具有最终必须发生的重要副作用并且静态对象必须由另一个静态对象的析构函数访问,您如何避免静态反初始化惨败?


(注意:FAQ-lite 提到这个问题在 M. Cline 和 G. Lomow 的 C++ FAQs: Frequently Asked Questions 的 FAQ 16.17 中得到了回答。我无法访问这本书,这是为什么我会问这个问题。)

【问题讨论】:

标签: c++ destructor static-initialization initialization-order


【解决方案1】:

只要其他对象的静态析构函数先运行,就可以了。您可以通过在“对象 A”之前构造另一个对象来确保这一点。只要两个对象在同一个编译单元中声明,它们就会按照它们在源代码中出现的顺序被初始化,并以相反的顺序被销毁。

如果您需要跨编译单元发生这种情况,那么您就不走运了。更好的是在运行时动态创建它们并在 main 结束时销毁它们,而不是使它们成为静态的。

【讨论】:

  • OP 专门引用了一个指向模式的链接,以避免全局变量位于同一编译单元中的必要性。使用相同的技术,他可以获得定义的构造顺序,从而获得定义的破坏顺序,因此他不会不走运。
【解决方案2】:

这有点小技巧,但我会添加一些静态布尔值来跟踪初始化的顺序然后最后结束的对象会进行清理,即使它不是所有者。

【讨论】:

    【解决方案3】:

    像全局对象这样的函数静态对象保证被销毁(假设它们被创建)。

    毁灭的顺序是创造的逆序。
    因此,如果一个对象在销毁期间依赖于另一个对象,您必须保证它仍然可用。这相对简单,因为您可以通过确保正确完成创建顺序来强制破坏顺序。

    以下链接是关于单例的,但描述了类似的情况及其解决方案:
    Finding C++ static initialization order problems

    根据常见问题解答 lite 中描述的惰性初始化全局变量的一般情况,我们可以解决如下问题:

    namespace B
    {
        class B { ... };
    
        B& getInstance_Bglob;
        {
            static B instance_Bglob;
            return instance_Bglob;;
        }
    
        B::~B()
        {
             A::getInstance_abc().doSomthing();
             // The object abc is accessed from the destructor.
             // Potential problem.
             // You must guarantee that abc is destroyed after this object.
             // To gurantee this you must make sure it is constructed first.
             // To do this just access the object from the constructor.
        }
    
        B::B()
        {
            A::getInstance_abc();
            // abc is now fully constructed.
            // This means it was constructed before this object.
            // This means it will be destroyed after this object.
            // This means it is safe to use from the destructor.
        }
    }
    namespace A
    {
        class A { ... };
    
        A& getInstance_abc()
        {
            static A instance_abc;
            return instance_abc;
        }
    }
    

    【讨论】:

    • C++0x 的修复之一是函数局部静态变量的确定性破坏顺序。所以在 C++03 中,即使 A 是在 B 之前构造的,你也不能保证 A 会在 B 之后被破坏。一个值得注意的例外是当 A 和 B 在同一个编译单元中定义时。
    • @Caspin:在当前标准“ISO/IEC 14882”中(openassist.googlecode.com/files/… 定义明确。参见“3.6.3 Termination”第 1 段:Destructors (12.4) 用于初始化对象静态存储持续时间(在块范围或命名空间范围内声明)作为从 main 返回的结果和作为调用 exit (18.3) 的结果被调用。这些对象以它们的构造函数完成或完成的相反顺序被销毁完成它们的动态初始化。
    • 看来你是对的。破坏顺序总是相反的构造顺序。我的印象是 C++ 只保证在一个编译单元内,而不是整个程序。我应该更频繁地阅读规范。我对 C++0x 的评论是我对新标准引入的新多线程销毁顺序感到困惑。
    猜你喜欢
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 2014-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多