J'implémentais un modèle singleton. Ici, je crée une nouvelle instance de Singleton * dans GetInstance. Lorsque j'essaie de la supprimer dans le destructeur, elle le fait en boucle infinie. Comment éviter les fuites de mémoire dans ce cas?
Veuillez vous référer au code ci-dessous:
#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;
}
Bien sûr, cela provoque une boucle infinie!
Vous appelez le destructeur, mais le destructeur appelle également le destructeur, de sorte que le destructeur appelle à nouveau le destructeur ... et encore ...
Si vous voulez utiliser delete
, vous devez l’utiliser à partir de outside du destructeur et NE PAS le rappeler dans le destructeur.
Pour ce faire, vous pouvez utiliser une autre méthode statique qui reflétera la méthode 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 resources if any.
...
}
Remarque: les autres personnes vous avertissent de l’utilisation d’un singleton et elles ont raison, car ce schéma est souvent mal utilisé. Alors réfléchissez avant de l'utiliser. Mais allez-y quand même, c'est le bon moyen d'apprendre!
Bien que la meilleure pratique n’utilise pas le modèle singleton dans la plupart des cas, il est recommandé d’utiliser des variables locales statiques dans des fonctions pour créer des singletons:
static Singleton& Singleton::GetInstance() {
static Singleton the_singleton;
return the_singleton;
}
Pour expliquer la meilleure pratique: Singleton-nage n’est généralement pas nécessaire, sauf si vous devez représenter une ressource véritablement mondiale. Les singletons souffrent de tous les inconvénients des variables globales (car ce sont des variables globales avec un glissement OO), et ont souvent peu de raisons d’être vraiment singuliers. Le programmeur naïf voudra peut-être implémenter God
en tant qu'objet singleton. Le programmeur avisé ne le fait pas et se réjouit lorsque le client s'avère être un polythéiste.
Voici une implémentation plus correcte de singletons:
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
};
L'instance statique est créée la première fois que vous appelez Instance()
et détruite à la fermeture du programme.
Mais méfiez-vous de l'utilisation de singletons. Ils ne sont pas diaboliques, comme certains le pensent (je trouve cette position irrationnelle), mais ils sont très faciles à mal utiliser et difficiles à utiliser correctement. En règle générale, n'utilisez pas de singletons pour vos "classes d'interface" (celles utilisées par d'autres parties du programme); essayez d’utiliser des singletons uniquement comme détails d’implémentation et uniquement lorsque cela vous semble approprié.
Edit: _ Un exemple d'utilisation
Il y a quelque temps, j'ai posté une réponse sur gamedev.stackexchange et la solution que j'ai proposée utilisait des singletons dans le cadre de la mise en œuvre, pas l'interface. Le code est commenté et explique pourquoi des singletons sont nécessaires: https://gamedev.stackexchange.com/a/17759/6188
Ajoutez un membre statique Singleton::DestroyInstance()
qui supprime l’instance et l’appelle depuis le répertoire principal.
void Singleton::DestroyInstance() {
delete m_pInstance;
m_pInstance = 0;
}
/* ...................... */
int main()
{
Singleton* pInstance = Singleton::GetInstance();
/* ... */
Singleton::DestroyInstance();
}
Réponse courte, n'utilisez pas de singletons.
Réponse plus longue, n'appelez jamais delete sur le pointeur singleton dans main()
. Utilisez une sorte d’objet statique qui supprimera le singleton lorsque d’autres variables globales sont appelées.