C ++ Singleton дизайн шаблона - PullRequest
652 голосов
/ 17 июня 2009

Недавно я столкнулся с реализацией / реализацией шаблона проектирования Singleton для C ++. Это выглядело так (я взял это из примера из реальной жизни):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

Из этого объявления я могу сделать вывод, что поле экземпляра инициируется в куче. Это означает, что есть выделение памяти. Что для меня совершенно неясно, когда именно память будет освобождена? Или есть ошибка и утечка памяти? Кажется, в реализации есть проблема.

Мой главный вопрос: как мне правильно его реализовать?

Ответы [ 18 ]

979 голосов
/ 17 июня 2009

В 2008 году я предоставил реализацию C ++ 98 шаблона проектирования Singleton, который вычисляется лениво, гарантированно уничтожается, не технически безопасен для потоков:
Может ли кто-нибудь предоставить мне образец синглтона на с ++?

Вот обновленная реализация C ++ 11 шаблона проектирования Singleton, которая вычисляется лениво, корректно уничтожается и поточно-безопасна .

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are unacceptable otherwise you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

См. Эту статью о том, когда использовать синглтон: (не часто)
Синглтон: как его использовать

См. Эти две статьи о порядке инициализации и о том, как справиться:
Порядок инициализации статических переменных
Поиск проблем порядка статической инициализации C ++

См. Эту статью, описывающую времена жизни:
Каково время жизни статической переменной в функции C ++?

См. Эту статью, в которой обсуждаются некоторые последствия многопоточности для синглетонов:
Экземпляр Singleton, объявленный как статическая переменная метода GetInstance, является ли он потокобезопасным?

См. Эту статью, в которой объясняется, почему двойная проверка блокировки не будет работать на C ++:
Каковы все распространенные неопределенные поведения, о которых должен знать программист C ++?
Доктор Доббс: C ++ и опасности двойной проверки блокировки: Часть I

45 голосов
/ 17 июня 2009

Будучи Синглтоном, вы обычно не хотите, чтобы он был разрушен.

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

33 голосов
/ 17 июня 2009

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

Я предпочитаю этот вид реализации (на самом деле, я не правильно сказал, что предпочитаю, потому что максимально избегаю синглетонов):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

У него нет динамического выделения памяти.

11 голосов
/ 31 октября 2016

@ Ответ Локи Астари превосходен.

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

В этом случае std::shared_ptr может использоваться для поддержки singleton для всех пользователей, даже если в конце программы вызываются статические деструкторы:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};
9 голосов
/ 17 июня 2009

Еще одна нераспределительная альтернатива: создайте синглтон, скажем, класса C, как вам нужно:

singleton<C>()

с помощью

template <class X>
X& singleton()
{
    static X x;
    return x;
}

Ни этот, ни ответ Кэтэлина не являются автоматически поточно-ориентированными в текущем C ++, но будут в C ++ 0x.

6 голосов
/ 18 июня 2009

Решение в принятом ответе имеет существенный недостаток - деструктор для синглтона вызывается после того, как элемент управления покидает функцию main(). На самом деле могут возникнуть проблемы, когда некоторые зависимые объекты размещены внутри main.

Я столкнулся с этой проблемой, когда пытался ввести Singleton в приложении Qt. Я решил, что все мои диалоговые окна настройки должны быть Singletons, и принял шаблон выше. К сожалению, основной класс Qt QApplication был размещен в стеке в функции main, и Qt запрещает создавать / уничтожать диалоги, когда объект приложения недоступен.

Вот почему я предпочитаю выделенные кучей синглтоны. Я предоставляю явные методы init() и term() для всех синглетонов и вызываю их внутри main. Таким образом, я полностью контролирую порядок создания / уничтожения синглетонов, а также гарантирую, что синглтоны будут созданы, независимо от того, позвонил ли кто-нибудь по номеру getInstance() или нет.

5 голосов
/ 12 января 2013

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

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);
4 голосов
/ 12 февраля 2018

Я не нашел реализацию CRTP среди ответов, поэтому вот она:

template<typename HeirT>
class Singleton
{
public:
    Singleton() = delete;

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;

    static HeirT &instance()
    {
        static HeirT instance;
        return instance;
    }
};

Чтобы использовать просто унаследуйте свой класс от этого, как: class Test : public Singleton<Test>

3 голосов
/ 08 мая 2014

Вот простая реализация.

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

Создан только один объект, и эта ссылка на объект возвращается каждый раз после слов.

SingletonClass instance created!
00915CB8
00915CB8

Здесь 00915CB8 - это место в памяти одноэлементного Объекта, одинаковое для продолжительности программы, но (обычно!) Разное при каждом запуске программы.

N.B. Это не потокобезопасный. Вы должны обеспечить безопасность потока.

2 голосов
/ 17 июня 2009

Это действительно, вероятно, выделено из кучи, но без источников нет способа узнать.

Типичная реализация (взятая из некоторого кода, который я уже имел в emacs) была бы:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...