Как удалить указатель Singleton?
Я реализовал одноэлементный шаблон. Вот, я создаю новый экземпляр Singleton * в GetInstance, когда я пытаюсь удалить его в деструкторе, он работает в бесконечном цикле. Как избежать утечки памяти в этом случае?
Пожалуйста, обратитесь к приведенному ниже фрагменту кода:
#define NULL 0
class Singleton
{
private :
static Singleton* m_pInstance;
Singleton(){};
public :
static Singleton* GetInstance()
{
if(m_pInstance == NULL)
{
m_pInstance = new Singleton();
}
return m_pInstance;
}
~Singleton()
{
//delete m_pInstance; // The system goes in infinate loop here if i uncomment this
m_pInstance = NULL;
}
};
Singleton* Singleton ::m_pInstance = NULL;
int main()
{
Singleton* pInstance = Singleton::GetInstance();
delete pInstance;
}
Ответы
Ответ 1
Конечно, это вызывает бесконечный цикл!
Вы вызываете деструктор, но деструктор также вызывает деструктор, поэтому деструктор снова вызывает деструктор... и снова...
Если вы хотите использовать delete, вы должны использовать его снаружи деструктора и НЕ называть его снова в деструкторе.
Для этого вы можете использовать другой статический метод, который будет отражать метод GetInstance():
class Singleton
{
public :
...
// this method is a mirror of GetInstance
static void ResetInstance()
{
delete m_pInstance; // REM : it works even if the pointer is NULL (does nothing then)
m_pInstance = NULL; // so GetInstance will still work.
}
...
~Singleton()
{
// do destructor stuff : free allocated ressources if any.
...
}
Примечание: другие люди предупреждают вас об использовании синглета, и они правы, потому что этот шаблон часто используется неправильно. Поэтому подумайте, прежде чем использовать его. Но идти в любом случае, это хороший способ узнать!
Ответ 2
В то время как лучшая практика не использует одноэлементный шаблон в большинстве случаев, хорошей практикой является использование статических локальных переменных в функциях для создания синглетонов:
static Singleton& Singleton::GetInstance() {
static Singleton the_singleton;
return the_singleton;
}
Чтобы дать некоторое обоснование лучшей практике: Singleton-nage обычно не нужен, если вы не должны представлять действительно глобальный ресурс. Синглтоны страдают от всех недостатков глобальных переменных (поскольку они являются глобальными переменными с некоторой обледенением OO), и часто имеют мало оснований быть действительно единственными. Наивный программист может захотеть реализовать God
как одноэлементный объект. Мудрый программист не радуется и радуется, когда клиент оказывается политеистом.
Ответ 3
Здесь более правильная реализация синглетонов:
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton inst;
return inst;
}
protected:
Singleton(); // Prevent construction
Singleton(const Singleton&); // Prevent construction by copying
Singleton& operator=(const Singleton&); // Prevent assignment
~Singleton(); // Prevent unwanted destruction
};
Статический экземпляр создается при первом вызове Instance()
и уничтожается при закрытии программы.
Но будьте осторожны с использованием синглтонов. Они не злые, как некоторые считают, что это (я нахожу эту позицию иррациональной), но их очень легко злоупотреблять и трудно использовать правильно. Как правило, не используйте синглтоны для ваших "интерфейсных классов" (те, которые используются другими частями программы); попробуйте использовать синглтоны только как детали реализации и только тогда, когда это будет удобно.
Изменить: Пример использования
Некоторое время назад я опубликовал ответ на gamedev.stackexchange, и предлагаемое мной решение использовало одиночные игры как часть реализации, а не интерфейс. Код комментируется и объясняет, почему нужны одиночные игры: https://gamedev.stackexchange.com/a/17759/6188
Ответ 4
Добавить статический член Singleton::DestroyInstance()
, который удаляет экземпляр и вызывает его из основного.
void Singleton::DestroyInstance() {
delete m_pInstance;
m_pInstance = 0;
}
/* ...................... */
int main()
{
Singleton* pInstance = Singleton::GetInstance();
/* ... */
Singleton::DestroyInstance();
}
Ответ 5
Короткий ответ, не используйте синглтоны.
Более длинный ответ, никогда не вызывайте delete по указателю singleton в main()
. Используйте какой-то статический объект, который будет удалять синглтон при вызове других глобальных переменных dtors.
Ответ 6
используя SingletonHolder of Loki-Library, написанную Андреем Александреску.
#include "SingletonHolder"
class Singleton
{
//not allowed ctor
private:
Singleton()
{}
~Singleton()
{}
...
//Singelton on heap
friend struct Loki::CreateUsingNew<Singleton>;
}
Singleton& get_singleton_pointer()
{
return Loki::SingltonHolder<Singleton>::Instance();
}
В этом примере Singlton будет удален во время завершения программы.
Существует также другая структура, чтобы создать указатель одиночного элемента, используя malloc, static...
более подробно посмотрите:
http://loki-lib.sourceforge.net/html/a00628.html
Alternativ вы можете создать синглтон, используя статическую переменную:
template <typename T>
struct CreateUsingStatic
{
static T& get_T_singleton()
{
static T t;
return t;
}
};
class Singleton
{
...
private:
friend struct CreateUsingStatic<Singleton>;
}