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 ]

2 голосов
/ 10 апреля 2018

Кто-нибудь упоминал std::call_once и std::once_flag? Большинство других подходов, включая двойную проверку блокировки, не работают.

Одной из основных проблем в реализации одноэлементных шаблонов является безопасная инициализация. Единственным безопасным способом является защита последовательности инициализации с помощью синхронизирующих барьеров. Но сами эти барьеры должны быть благополучно инициированы. std::once_flag - это механизм гарантированной безопасной инициализации.

1 голос
/ 06 ноября 2015

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

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

Теперь где-то внутри функции (например, main) вы можете сделать:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

Ссылкам не нужно хранить указатель обратно на их Store, потому что эта информация предоставляется во время компиляции. Вам также не нужно беспокоиться о времени жизни Store, потому что компилятор требует, чтобы он был глобальным. Если в действительности существует только один экземпляр Store, то в этом подходе нет накладных расходов; с более чем одним экземпляром компилятор должен быть умным в отношении генерации кода. При необходимости класс ItemRef можно даже сделать friend из Store (вы можете иметь шаблонных друзей!).

Если Store сам по себе является шаблонным классом, то ситуация становится более запутанной, но все еще возможно использовать этот метод, возможно, путем реализации вспомогательного класса со следующей сигнатурой:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

Теперь пользователь может создать тип (и глобальный экземпляр) StoreWrapper для каждого глобального экземпляра Store и всегда получать доступ к хранилищам через свой экземпляр-обертку (таким образом, забывая о мрачных деталях параметров шаблона, необходимых для использования Store).

0 голосов
/ 18 января 2014
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

Пример:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);
0 голосов
/ 18 октября 2011

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

0 голосов
/ 18 августа 2011

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

0 голосов
/ 01 апреля 2010

Речь идет об управлении жизненным циклом объекта. Предположим, у вас есть больше, чем синглтонов в вашем программном обеспечении. И они зависят от синглтона Logger. Предположим, что во время уничтожения приложения другой одноэлементный объект использует Logger для регистрации шагов его уничтожения. Вы должны гарантировать, что Logger должен быть очищен последним. Поэтому, пожалуйста, ознакомьтесь с этой статьей: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf

0 голосов
/ 11 сентября 2018

Простой синглтон-класс. Это должен быть ваш файл класса заголовка

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

Получите доступ к вашему синглтону так:

sSingletonClass->Relocate(1, 2, 5);
0 голосов
/ 24 февраля 2012

Как насчет использования нового размещения, как это:

class singleton
{
    static singleton *s;
    static unsigned char *buffer[sizeof(singleton)/4 *4] //4 byte align
    static singleton* getinstance()
    {
        if (s == null)
        {
            s = new(buffer) singleton;
        }
        return s;
    }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...