2013-03-09 3 views
3

У меня уже есть некоторые классы одноэлементные, который использует указатели:Pitfall: Статическая переменная стека Singleton класс

class Logger 
{ 
public: 

    static Logger* Instance() 
    { 
     if (!m_pInstance) m_pInstance = new Logger; 
     return m_pInstance; 
    } 
private: 
    Logger(); 
    Logger(Logger const&); 
    Logger& operator=(Logger const&); 
    static Logger* m_pInstance; 
}; 

Но есть более простой способ, с помощью ссылки:

class Logger 
{ 
public: 
    static Logger& Instance() 
    { 
     static Logger theLogger; 
     return theLogger; 
    } 
private: 
    Logger(); 
    Logger(Logger const&); 
    Logger& operator=(Logger const&); 
    ~Logger(); 
}; 

Чтение статьи C++ Singleton design pattern , он предупреждает о втором способе:

[Potential Pitfall]: Эта форма синглтона может представлять проблему из-за продолжительности жизни объекта. Если один синглтон - , созданный в рамках другого, нужно быть в курсе последовательности вызовов деструктора .

Но я не могу это понять, может ли кто-нибудь показать мне плохое использование этого, что я должен его избегать?

+0

Насколько я знаю, если компилятор включит вашу функцию (в данном случае экземпляр), и если вы включите заголовок в более чем один файл (в вашем случае это выглядит вероятным), у вас будет более одного экземпляра (насколько я запомнить). Конечно, я не уверен, правильно ли я помню, поэтому лучше обратиться к More Effective C++ от Scott Meyers. –

+0

Проблема с обоими из вышеперечисленных событий - это то, что происходит, когда два потока одновременно называют экземпляр(). Компиляторы Pre C++ 11 иногда могли бы построить theLogger дважды. –

+0

Нет, этого не произойдет. Это на самом деле называется Mayer 'Singleton и безопасно, как это может быть –

ответ

7

Существует, действительно, проблема с обоими вариантами.

Типичный Синглтон

class Logger 
{ 
public: 

    static Logger* Instance() 
    { 
     if (!m_pInstance) m_pInstance = new Logger; 
     return m_pInstance; 
    } 
private: 
    Logger(); 
    Logger(Logger const&); 
    Logger& operator=(Logger const&); 
    static Logger* m_pInstance; 
}; 

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

Мейера Singleton

class Logger 
{ 
public: 
    static Logger& Instance() 
    { 
     static Logger theLogger; 
     return theLogger; 
    } 
private: 
    Logger(); 
    Logger(Logger const&); 
    Logger& operator=(Logger const&); 
    ~Logger(); 
}; 

Проблема с Singleton Майер действительно из-за разрушения объектов. После того, как вы вернетесь из main, деструктор всех глобальных переменных будет работать в обратном порядке, в котором были построены эти глобальные глобальные значения. . Это , как будто эти глобальные блоки были построены на стеке.

Если объект был создан до вашего регистратора и пытается использовать его в своем собственном деструкторе; то он будет использовать уже разрушенный объект, что приведет к неопределенному поведению.

Точнее, в обратном порядке их конструктор завершен.


Самый простой альтернативой является вернуться к Типичные Singleton:

class Logger { 
public: 
    static Logger& Instance() { 
     static Logger* L = new Logger; 
     return *L; 
    } 
private: 
    ... 
}; 

Это теперь потокобезопасной, но все же просачивается. Но на самом деле утечка здесь желательна, поскольку она гарантирует, что этот объект остается живым дольше любого другого, деструктор которого будет вызван. Предупреждение: если вам когда-либо было что-то делать в деструкторе, оно никогда не запустится.

Существуют и другие варианты, например, «Финикс Синглэнд» Александреску возвращается из пепла, если он вам понадобится после его смерти; однако получить как безопасное поведение во время разрушения, так и безопасность нитей и трудно.

Смежные вопросы