В C ++, как инициализировать статический член частного класса, объявленного внутри шаблона Singleton? - PullRequest
3 голосов
/ 13 октября 2010

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

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

Вот мой фактический код (не работает):

template <typename T> class Singleton
{
private:
    class PointerInstance
    {
    private:
        T* instance;
    public:
        PointerInstance() : instance(0) {}
        ~PointerInstance() { delete instance; } // no memory leak !
        T* Get()
        {
            if ( !instance ) {
                instance = new T();
            }
            return instance;
        }
    };
    static PointerInstance PInstance;
public:
    static T* pGetInstance(void)
    {
        return PInstance.pGet();
    };
protected:
    Singleton(void){};
    ~Singleton(void){};
};

А вот как должно выглядеть типичное объявление производного класса:

class Child : public Singleton<Child>
{
    friend class Singleton<Child>;
    Child();
    // etc...
};

По сути, не хватает экземпляра PInstance для каждого класса T, который я создаю как синглтон.

У меня вопрос: есть ли способ сделать это раз и навсегда с помощью нескольких общих строк кода в Singleton.h, содержащих приведенный выше код, или у меня нет другого выбора, кроме как добавить несколько конкретных строк код для каждого производного класса?

(Бонус: есть ли лучший способ сделать класс Singleton в C ++?)

Ответы [ 4 ]

5 голосов
/ 13 октября 2010
template <typename T> 
typename Singleton<T>::PointerInstance Singleton<T>::PInstance;

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

2 голосов
/ 13 октября 2010

Вот более простой способ написания синглтона CRTP без утечки памяти:

template <class T>
class Singleton
{
  friend class T;
private:
  Singleton() {};
  ~Singleton() {};
  Singleton(const Singleton&); // not implemented
  const Singleton& operator=(const Singleton&); // not implemented

public:
  static T* pGetInstance()
  {
    static T theInstance;
    return &theInstance;
  }
};

Использование такое же, как в вопросе.

0 голосов
/ 13 октября 2010

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

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

Если у вас есть такая возможность, лучший способ ее инициализировать - использовать boost :: Once.Вам нужно сделать что-то подобное в вашем источнике, где Foo - ваш класс.

все эти 3 статически объявлены в Foo и private

Foo* Foo::instance = NULL;
boost::once_flag Foo::flag = BOOST_ONCE_INIT;
void Foo::init()
{
   Foo::instance = new Foo;
};

Это также статический и публичный методчтобы получить экземпляр

Foo & Foo::getInstance()
{
   boost::call_once(init, Foo::flag);
   return *Foo::instance;
}

Конструктор Foo не должен бросать.

Обратите внимание, что эту технику можно использовать более широко для поточно-ориентированных ситуаций с ленивой оценкой после загрузки, а не только для одиночных вычислений.Новая версия boost :: Once принимает сначала флаг (который теперь представляет собой struct: beware), а во-вторых, принимает boost :: function, чтобы вы могли увеличить :: bind в информации для создания.

Для удаления вашегоСинглтон, вы можете создать boost :: shared_ptr на уровне модуля компиляции и привязать к нему указатель, а также использовать пользовательское средство удаления, являющееся статическим членом вашего класса, чтобы удаление могло оставаться закрытым.Ваш удалитель сможет вызывать delete, а ваша функция init будет иметь доступ к функции delete (которая также является закрытой), чтобы инициализировать с ней shared_ptr.

0 голосов
/ 13 октября 2010

Может быть, вы хотите взглянуть на Loki , который уже имеет общий класс SingletonHolder<>?

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