ProgrammationDéveloppeur C++

Qu'est-ce que le modèle de conception 'Singleton' en C++ ? Comment le mettre en œuvre correctement et quels sont les principaux pièges ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question :

Le modèle Singleton a été proposé pour limiter la création d'une seule instance d'une classe donnée, ce qui était nécessaire pour mettre en œuvre des gestionnaires globaux (par exemple, des enregistreurs, des pools de ressources, des configureurs).

Problème :

La mise en œuvre du Singleton semble simple, mais en C++, des difficultés surgissent : la sécurité des threads, la destruction correcte, l'ordre d'initialisation.

Solution :

La manière la plus sûre et moderne de mettre en œuvre le Singleton utilise une variable locale statique à l'intérieur d'une fonction statique, garantissant une initialisation au premier accès, la sécurité des threads (à partir de C++11) et une destruction correcte.

Exemple de code :

class Singleton { public: static Singleton& instance() { static Singleton s; return s; } void doSomething() {} private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };

Caractéristiques clés :

  • Garantie de l'existence d'une seule instance de la classe.
  • Création paresseuse de l'objet et sécurité des threads (à partir de C++11).
  • Contrôle de la durée de vie et de la destruction de l'instance.

Questions piégeuses.

Est-il possible de créer une deuxième instance du Singleton par sérialisation ou clonage ?

Oui. Si les méthodes de sérialisation/désérialisation sont implémentées ou si clone() est manuellement implémenté sans restreindre le constructeur de copie, une deuxième instance peut apparaître. Pour éviter cela, il est nécessaire d'interdire tous les moyens de copie, de clonage et de restauration par sérialisation.

Un Singleton sera-t-il correctement implémenté dans un environnement multithread en C++98/03 standard via une variable locale statique ?

Non. Les variables locales statiques avant C++11 ne garantissaient pas la sécurité des threads lors de l'initialisation. Cela pouvait entraîner la création de plusieurs instances si deux threads accédaient simultanément à la fonction instance(). En C++11 et ultérieur – le problème a été résolu au niveau de la norme.

Quand est détruite l'instance du Singleton créée via une variable locale statique ?

L'objet est détruit dans l'ordre inverse de sa création (LIFO) à la fin du programme (exit). Mais cela peut poser des problèmes si le destructeur accède à des objets déjà détruits.

Erreurs typiques et anti-modèles

  • Utiliser new au lieu de variables statiques, ce qui crée des fuites.
  • Ne pas interdire la copie/l'affectation.
  • Ne pas prendre en compte les threads (dans les anciennes normes).
  • Utilisation de shared_ptr ou weak_ptr pour stocker l'instance Singleton (violation de l'unicité).

Exemple de la vie réelle

Cas négatif

Dans un système de journalisation, le développeur implémente un Singleton à l'aide d'un pointeur global et de new, oubliant d'interdire la copie. Le programme fonctionne, mais dans un environnement multithread, différents instances de l'enregistreur apparaissent parfois, et les messages sont perdus.

Avantages :

  • Implémentation très simple ; fonctionne rapidement en mode unthread.

Inconvénients :

  • Fuites de mémoire potentielles.
  • Violation de l'unicité de l'instance dans les threads.
  • Problèmes de destruction.

Cas positif

Le Singleton est implémenté via une variable locale statique dans une fonction statique, la copie est interdite. La sécurité des threads est assurée, le programme est évolutif, les journaux sont correctement divisés.

Avantages :

  • Garantie d'unicité dans toutes les conditions.
  • Destruction correcte.
  • Pas de fuites de mémoire.

Inconvénients :

  • Plus difficile à tester (pour les tests unitaires, il est difficile de remplacer le singleton).