C++ G++;获取静态变量的销毁顺序错误

C++ G++;获取静态变量的销毁顺序错误,c++,templates,static,C++,Templates,Static,我有以下几个类试图实现一个通用的单例 struct BaseObject { virtual ~BaseObject() {} }; class _helper { private: template<typename T> friend class Singleton; set<BaseObject*> _s; static _helper& _get() { static _helper t;

我有以下几个类试图实现一个通用的单例

struct BaseObject
{
   virtual ~BaseObject() {}
};
class _helper
{
private:
    template<typename T> friend class Singleton;
    set<BaseObject*> _s;
    static _helper& _get()
    {
        static _helper t;
        return t;
    }
    _helper()
    {
        cout<<" _helper ctor"<<endl;
    }
    ~_helper()
    {
        cout<<" _helper dtor"<<endl;
        //assert(_s.empty());
    }
};

// Singleton<foo>::Instance() returns a unique instance of foo
template <typename T>
class Singleton : virtual private T
{
public:
    static T& Instance()
    {
        static Singleton<T> _T;
        return _T;
    } 

private:
    Singleton()
    {
        cout<<"inserting into helper "<<typeid(T).name()<<" ptr "<<this<<endl;
        assert(!_helper::_get()._s.count(this));
        _helper::_get()._s.insert(this);
    }
    ~Singleton()
    {
        cout<<"erasing from helper "<<typeid(T).name()<<" ptr "<<this<<endl;
        assert(_helper::_get()._s.count(this));
        _helper::_get()._s.erase(this);
    }
};
然而,在某些情况下,我看到以下情况:

 inserting into helper 3bar ptr 0x509630  
 _helper ctor  
 inserting into helper 3foo ptr 0x509588  
 erasing from helper 3bar ptr 0x509630  
 _helper dtor  
 erasing from helper 3foo ptr 0x509588  
请注意,在第二种情况下,
bar
foo
被破坏的顺序与它们的构造顺序相同。当
foo
bar
单例在共享库(.so)内实例化为静态引用时,似乎会发生这种情况:

static bar& b = Singleton<bar>::Instance();   
static foo& f = Singleton<foo>::Instance();   
static bar&b=Singleton::Instance();
静态foo&f=Singleton::Instance();

你知道为什么会这样吗?

如果单例和辅助对象位于不同的翻译单元或不同的共享对象中,就会发生这种情况。请记住,很难预测模板实例将在哪个翻译单元中结束。还要记住,每个共享对象都可以获得自己的实例,比如说
Singleton::\u T
。因此,您有一些每共享对象单例(IMHO不是很有用)

请注意,在将最后一个对象从辅助对象中移除之前,辅助对象将被销毁。这将导致程序在退出时崩溃。是的,正是这件事发生在我身上。您需要在_helper类中实现一个对象计数器,以便在至少有一个对象注册之前不会销毁它。或者,分配堆上的所有单例,并让助手在其生命周期结束时销毁它们


更新如果两个静态对象属于同一个动态库,则此销毁顺序反转可能不会发生。它肯定可以而且确实会发生。建议程序员不要跨动态库边界导出静态对象。

static\t定义了一个地址

static _helper& _get()
{
    static _helper t;
    return t;
}
但看起来您正试图将其用于两个不同的对象

无论如何,我从来没有见过一个模板被用于一个单身汉。在你的情况下,看起来你想毁掉一个单身汉。我也不记得以前见过。单例通常创建一次,并一直保留到您离开该程序为止(并且在您离开时仍然分配)


否则,您可能需要查看共享指针或侵入式引用计数对象?

它们是否在同一个翻译单元中?我必须在这里提出一些观点。这是对单例模式的错误应用。singleton模式应该严格保留给那些必须在程序中单独存在的对象,而不是应用于某个恰好在程序中只存在一次的对象。如果您想要一个通用的单例模式工具,那么您应该继承它,使对象不能存在于多个实例中。如果你需要一个全局的,使用一个。单身汉!=全局变量。@PaulR:标准要求构造和销毁的顺序颠倒,并且它还要求在单个转换单元内保持定义的顺序(对于全局静态)。对于函数静态,构造的顺序与调用函数的顺序相同,但销毁的顺序仍应与构造的顺序相反。@GManNickG:C++11中的措辞有点复杂(由于线程静态对象),但在C++03中3.6.3p1非常清楚:析构函数(12.4)对于静态存储持续时间为[…]的初始化对象,这些对象的销毁顺序与其构造函数的完成顺序或动态初始化的完成顺序相反。没有提到不同的翻译单位。重要的一条信息是,虽然翻译单元的构造顺序没有定义,但销毁顺序肯定是相反的。@johngull:如果你仔细阅读最后一次转储,您将看到顺序不一致:
Singleton
Singleton
之前构造和
Singleton
Singleton
之前销毁,而它应该是(缩写):
S,S=>~S,~S
。这就是不一致性,对于那些运行,实现似乎没有生成正确的终止代码。即使在这种情况下也不应该发生。跨TU边界的构造顺序尚未定义,但标准要求销毁顺序与相应构造函数(或动态初始化)的完成顺序相反。也就是说,给定两个翻译单元中的两个静态对象
a
b
,该标准需要两种可能的顺序之一:
a,b,~b,~a
b,a,~a,~b
,这正是我认为应该的情况。该标准没有提到动态加载的库。实际上,当卸载这样的库时,它“拥有”的所有静态对象都会被销毁,而不管其他.so/.dll中的静态对象是如何构造的。我没有使用dlopen/dlclose加载共享库。我本以为常规使用不会有这个问题。理论上可能有两种不同的销毁顺序,一种用于
dlopen
,另一种用于“常规”使用。但不是这样做的。我知道,我被生产代码中的这种行为咬了一口。一个静态变量被移动到另一个库中,突然所有的东西都开始在终止时转储core。
static _helper& _get()
{
    static _helper t;
    return t;
}