G++ получает порядок разрушения статических переменных

У меня есть следующие классы, которые пытаются реализовать общий синглтон.

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

Теперь, если я вызываю Singleton< bar>::Instance(), а затем Singleton< foo>::Instance(), я должен увидеть следующий вывод:

 inserting into helper 3bar ptr 0x509630  
 _helper ctor  
 inserting into helper 3foo ptr 0x509588  
 erasing from helper 3foo ptr 0x509588  
 erasing from helper 3bar ptr 0x509630  
 _helper dtor  

Однако в некоторых случаях я вижу следующее:

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

Любые идеи, почему бы это сделать?

Ответ 1

Это может произойти, если синглтоны и помощник расположены в разных единицах перевода или разных общих объектах. Имейте в виду, что довольно сложно предсказать, в какой части перевода будет создан экземпляр шаблона. Также помните, что каждый общий объект может получить свой собственный экземпляр say Singleton<foo>::_T. Таким образом, у вас есть какие-то однопользовательские одноэлементы (не очень полезный ИМХО).

Обратите внимание, что ваш помощник уничтожен до, последний объект удаляется из него. Это приведет к сбою программы при выходе. Да, эта точная вещь произошла со мной. Вам нужно будет реализовать счетчик объектов в классе _helper, чтобы он не был уничтожен, пока не будет зарегистрирован хотя бы один объект. В качестве альтернативы, выделите все синглтоны в куче и позвольте помощнику уничтожить их, когда закончится его время жизни.

update Этот разворот порядка уничтожения, вероятно, не может произойти, если два статических объекта принадлежат одной и той же динамической библиотеке. Это определенно может и произойдет иначе. Здесь программистам рекомендуется не экспортировать статические объекты через границы динамической библиотеки.

Ответ 2

static _helper t; определяет один адрес, насколько я могу судить.

static _helper& _get()
{
    static _helper t;
    return t;
}

Но похоже, что вы пытаетесь использовать его для двух разных объектов.

Во всяком случае, я никогда не видел, чтобы шаблон использовался для синглета. И в вашем случае похоже, что вы пытаетесь уничтожить синглтон. Этого я тоже не помню. Одноэлемент обычно создается один раз и остается вокруг, пока вы не покинете программу (и по-прежнему выделяется, когда вы уходите.)

В противном случае вы можете захотеть просмотреть общие указатели или навязчивые подсчитанные объекты?