单例破坏的atexit:失败案例

atexit for singleton destruction : failure case

本文关键字:失败 案例 atexit 单例破      更新时间:2023-10-16

来源:https://sourcemaking.com/design_patterns/to_kill_a_singleton

有一点是肯定的:如果单例析构函数相互依赖。另一种选择是完全避开驱逐舰,而依赖草案标准atexit()函数,正如Tim Peierls向我建议的那样:我坚持atexit()是一个很好的方法来清理C++中的singleton想要具有程序生存期且没有替换的单个实例。

标准草案承诺了很多:函数atexit()可以是用于指定要在退出时调用的函数。如果atexit()调用时,实现不应破坏初始化的对象在atexit()调用之前,直到在已调用atexit()调用。

我能看到这种失败的唯一方法是如果其析构函数依赖于Singleton实例的对象已初始化之后,构造Singleton实例,即通过其他一些静态初始化。这表明类具有静态实例应避免在销毁过程中依赖于singleton。(或者至少应该有一种方法让这些类检查辛格尔顿在毁灭过程中的存在。)

我无法理解最后一段,即在什么情况下会失败以及如何失败。

有人能给它点光吗?

由于使用atexit而不是析构函数来清理Singleton,因此可以更改对象清理的顺序。例如:

Singleton S;
Object O;
// later in code:
Call atexit() to register cleanup function for S

通常,这些对象的销毁顺序是O,然后是S,但添加了atexit调用后,这是相反的,因此S在atexit调用中被清除,然后O被销毁。如果O的析构函数以任何方式依赖于Singleton s,那么在该析构函数运行时,您将有未定义的行为。

避免这种情况的方法是在构造任何依赖于它的对象之前,调用atexit来注册Singleton清理函数。如果O本身是一个静态对象,这可能很棘手,并且可能需要创建一个类,该类的构造函数调用atexit,以便将其插入两个静态对象之间。

Singleton S;
struct SAtExit {
     SAtExit() { atexit(...); }
} SCleanup;
Object O;