RAII для синглтона - PullRequest
       30

RAII для синглтона

2 голосов
/ 19 декабря 2009

У меня есть одноэлементный класс, экземпляр которого инициализируется в глобальной области видимости в файле CPP класса:

Singleton* Singleton::uniqueInstance = new Singleton();

Его заголовочный файл выглядит так:

class Singleton {
public:
    static Singleton& getInstance() { return *uniqueInstance; }
    static bool destroyInstance() { delete uniqueInstance; }

private:
    //...
    //... typical singleton stuff
    static Singleton* uniqueInstance;
}; // end of class Singleton

Я заметил, что его деструктор не запускается во время завершения программы, поэтому я добавил открытый статический интерфейс Singleton::destroyInstance(), который вручную вызывается клиентским кодом до выхода из программы, например, удаление. Этот фрагмент не является полным кодом, и предполагается, что существуют другие коды, связанные с безопасностью потоков. В этом случае, как я могу использовать RAII, чтобы устранить необходимость введения такого интерфейса? Спасибо за совет.

Ответы [ 6 ]

3 голосов
/ 19 декабря 2009

Деструктор не будет вызываться автоматически, потому что глобальный объект - это указатель , а не сам объект. Если бы вы объявили глобальный объект следующим образом:

Singleton Singleton::uniqueInstance();

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

2 голосов
/ 19 декабря 2009

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

В любом случае, очень упрощенный вариант таков:

class MySingleton
{
public:
  static MySingleton& Intance() { static MySingleton M_Instance; return M_Instance; }
private:
  MySingleton() {}
};

Это работает хорошо, если вы не используете MI и не используете синглтон во время глобальных разрушений. Кроме того, он не работает по крайней мере с VC2003 (используется для создания по одному экземпляру для каждой библиотеки, которую вызывал метод ....), я не знаю, с более поздними версиями, мы вообще перестали компилировать в Windows некоторое время назад.

Теперь, если вы хотите больше узнать о Синглтоне:

  • вопросы создания и уничтожения
  • Сорт жизни
  • Поток безопасности

Александреску сконцентрировался на этом для полной главы Современный дизайн C ++ , если у вас нет доступа к книге, вы все равно можете прочитать код, полученный в результате отражения в Локи .

Кроме того, вы можете просто использовать альтернативные подходы к дизайну. Синглтон может затруднить тестирование, об этом были интересные мысли от людей, которые поддерживают Dependency Injection. Загляните в Misko Hevery запись в блоге .

0 голосов
/ 20 декабря 2009

У вас есть два решения:

Ваш объект может быть построен до "основного"

... тогда сделайте его глобальным объектом:

// header
class Singleton {
public:
   static Singleton& getInstance() ;
   // ...

private:
    //...
}; // end of class

.

// source
Singleton g_singleton ;

Singleton& Singleton::getInstance()
{
    return g_singleton ;
}

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

Ваш объект должен быть построен после «основного» * ​​1013 * ... затем поместите его в умный указатель: // header class Singleton { public: static Singleton& getInstance() { if(uniqueInstance.get() == 0) { uniqueInstance.reset(new Singleton()) ; } return *uniqueInstance; } private: //... //... typical singleton stuff static std::auto_ptr<Singleton> uniqueInstance; }; // end of class Singleton . // source std::auto_ptr<Singleton> Singleton::uniqueInstance ; Таким образом, экземпляр будет создан при первом использовании, а затем будет уничтожен деструктором интеллектуального указателя в конце выполнения. У вас все еще есть проблема многопоточного доступа для создания. Вот почему я предпочитаю первое решение.

0 голосов
/ 19 декабря 2009

Для одноэлементного шаблона убедитесь, что вы определили закрытый конструктор , как этот, чтобы клиент не мог создавать много объектов:

class Singleton {
public:
    static Singleton& getInstance() { 

      if(!uniqueInstance)
         uniqueInstance = new Singleton();

      return *uniqueInstance; 
    }

    static bool destroyInstance() { delete uniqueInstance; }

private:
    Singleton() { }; //  
    static Singleton* uniqueInstance;
};

Singleton* Singleton::uniqueInstance(NULL);
0 голосов
/ 19 декабря 2009

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

0 голосов
/ 19 декабря 2009

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

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

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