Singleton c ++ vs object statique global

Un de mes amis m’a demandé aujourd’hui pourquoi préférerait-il utiliser singleton sur un object statique global? La façon dont je l’ai commencé à expliquer était que le singleton peut avoir un état vs un object global statique, mais je n’étais pas sûr .. parce que cela en C ++ .. (je venais de C #)

Quels sont les avantages les uns par rapport aux autres? (en C ++)

En fait, en mode C ++, il s’agit d’un object statique local.

Printer & thePrinter() { static Printer printer; return printer; } 

C’est techniquement un singleton cependant, cette fonction peut même être une méthode statique d’une classe. Il garantit donc d’être construit avant d’être utilisé, contrairement aux objects statiques globaux, qui peuvent être créés dans n’importe quel ordre, ce qui permet d’échouer de manière non cohérente lorsqu’un object global en utilise un autre, un scénario assez commun.

Ce qui le rend meilleur que les méthodes courantes pour créer de nouvelles instances en appelant new c’est que le destructeur d’object soit appelé à la fin d’un programme. Cela n’arrivera pas avec un singleton alloué dynamicment.

Un autre aspect positif est qu’il est impossible d’accéder à singleton avant qu’il ne soit créé, même à partir d’autres méthodes statiques ou de sous-classes. Vous permet d’économiser du temps de débogage.

En C ++, l’ordre d’instanciation des objects statiques dans différentes unités de compilation n’est pas défini. Ainsi, il est possible pour un global de faire référence à un autre qui n’est pas construit, en faisant exploser votre programme. Le motif singleton supprime ce problème en liant la construction à une fonction membre statique ou à une fonction libre.

Il y a un bon résumé ici .

Un de mes amis m’a demandé aujourd’hui pourquoi préférerait-il utiliser singleton sur un object statique global? La façon dont je l’ai commencé à expliquer était que le singleton peut avoir un état vs un object global statique, mais je n’étais pas sûr .. parce que cela en C ++ .. (je venais de C #)

Un object global statique peut également avoir l’état C #:

 class myclass { // can have state // ... public static myclass m = new myclass(); // globally accessible static instance, which can have state } 

Quels sont les avantages les uns par rapport aux autres? (en C ++)

Un singleton paralyse votre code, contrairement à une instance statique globale. Il existe d’innombrables questions sur SO concernant les problèmes avec les singletons déjà. Voici l’un et l’autre , ou un autre .

En bref, un singleton vous donne deux choses:

  • un object accessible globalement, et
  • une garantie que seule une instance peut être créée.

Si nous voulons juste le premier point, nous devrions créer un object accessible globalement. Et pourquoi voudrions-nous jamais le second? Nous ne soaps pas à l’avance comment notre code pourrait être utilisé à l’avenir, alors pourquoi le supprimer et supprimer ce qui pourrait être une fonctionnalité utile? Nous avons généralement tort quand nous prédisons que “je n’aurai besoin que d’une instance”. Et il y a une grande différence entre “je n’aurai besoin que d’une seule instance” (la réponse correcte est alors de créer une instance), et “l’application ne peut en aucun cas s’exécuter correctement si plus d’une instance est créée. formater le disque dur de l’utilisateur et publier des données sensibles sur Internet “(la réponse est alors: votre application est probablement cassée, mais si ce n’est pas le cas, alors oui, un singleton est ce dont vous avez besoin)

Raison 1:
Les singletons sont faciles à faire, donc ils sont construits paresseux.
Bien que vous puissiez faire cela avec globals, cela demande un travail supplémentaire de la part du développeur. Donc, par défaut, les globales sont toujours initialisés (à l’exception de certaines règles spéciales avec des espaces de noms).

Donc, si votre object est volumineux et / ou coûteux à construire, vous ne voudrez peut-être pas le construire à moins que vous ne deviez vraiment l’utiliser.

Raison 2:
Problème d’ordre d’initialisation (et de destruction).

 GlobalRes& getGlobalRes() { static GlobalRes instance; // Lazily initialized. return instance; } GlobalResTwo& getGlobalResTwo() { static GlobalResTwo instance; // Lazy again. return instance; } // Order of destruction problem. // The destructor of this object uses another global object so // the order of destruction is important. class GlobalResTwo { public: GlobalResTwo() { getGlobalRes(); // At this point globalRes is fully initialized. // Because it is fully initialized before this object it will be destroyed // after this object is destroyed (Guaranteed) } ~GlobalResTwo() { // It is safe to use globalRes because we know it will not be destroyed // before this object. getGlobalRes().doStuff(); } }; 

Un autre avantage du Singleton sur l’object statique global est que, parce que le constructeur est privé, il existe une directive très claire, imposée par le compilateur, qui dit “Il ne peut y en avoir qu’un”.

En comparaison, avec l’object statique global, rien n’empêche un code d’écriture de développeur qui crée une instance supplémentaire de cet object.

L’avantage de la contrainte supplémentaire est que vous avez une garantie quant à la manière dont l’object sera utilisé.

En utilisant Singleton (“construire à la première utilisation”) idiome, vous pouvez éviter le fiasco de l’ordre d’initialisation statique

En C ++, il n’y a pas beaucoup de différence entre les deux en termes d’utilité réelle. Un object global peut bien sûr conserver son propre état (éventuellement avec d’autres variables globales, même si je ne le recommande pas). Si vous utilisez un singleton ou un global (et il y a plusieurs raisons de ne pas le faire), la plus grande raison d’utiliser un singleton sur un object global est qu’avec un singleton, vous pouvez avoir un polymorphism dynamic en ayant plusieurs classes héritant de une classe de base singleton.

OK, il y a deux raisons pour aller avec un singleton vraiment. L’une est la chose d’ordre statique dont tout le monde parle.

L’autre est d’empêcher quelqu’un de faire quelque chose comme ça en utilisant votre code:

 CoolThing blah; gs_coolGlobalStaticThing = blah; 

ou pire encore:

 gs_coolGlobalStaticThing = {}; 

L’aspect encapsulation protégera votre instance des idiots et des saccades malveillantes.