IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Singleton en C++


Sujet :

C++

  1. #1
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2005
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 162
    Par défaut Singleton en C++
    Bonjour,

    Je me suis toujours demande (dans un contexte monothread) pourquoi, pour écrire un singleton Widget, on ne fait généralement pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    static Widget& instance()
    {
      static Widget w;
      return w;
    }
    ou bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    static Widget& instance()
    {
      static Widget* w = new Widget();
      return *w;
    }

    plutôt qu'utiliser un membre static de la classe Widget :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    class Widget
    {
    public:
    ...
     
    static Widget& instance()
    {
      if (!w)
      {
        w = new Widget;
      }
      return *w;
    }
    ...
     
    private:
    static Widget* w;
    };
     
    Widget* Widget::w = 0;
    Ce qui est qd même plus long à écrire ...

  2. #2
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Par défaut
    salut,
    dans ton cas tu n'as besoin apparemment que d'une fonction globale ,
    mais si ton singleton propose d'autres services comme la libération de la ressource allouée, dans ce cas une classe est plus pratique qu'une fonction globale.
    la deuxième forme d'allocation de ta ressource peut être obligatoire si elle a besoin d'un contexte précis pour être instanciée. (dépendance avec une autre ressource de l'application).
    donc ça depend de la ressource a partager ,si elle est autonome ou non,
    autre point avec des outils comme intellisense (visual c++) je préfère passer par une classe c'est plus pratique a l'utilisation mais plus long a écrire...

  3. #3
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par vandamme Voir le message
    Bonjour,

    Je me suis toujours demande (dans un contexte monothread) pourquoi, pour écrire un singleton Widget, on ne fait généralement pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    static Widget& instance()
    {
      static Widget w;
      return w;
    }
    C'est le singleton de Meyer.

    Citation Envoyé par vandamme Voir le message
    ou bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    static Widget& instance()
    {
      static Widget* w = new Widget();
      return *w;
    }
    Ca, c'est une des formes canoniques.

    Citation Envoyé par vandamme Voir le message
    plutôt qu'utiliser un membre static de la classe Widget :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    class Widget
    {
    public:
    ...
     
    static Widget& instance()
    {
      if (!w)
      {
        w = new Widget;
      }
      return *w;
    }
    ...
     
    private:
    static Widget* w;
    };
     
    Widget* Widget::w = 0;
    Ce qui est qd même plus long à écrire ...
    Et ça, c'est le singleton tel qu'il est présenté dans le GoF95 - la forme canonique de base.

    Chacune des implémentations a sa spécificité (les deux formes canoniques sont équivalentes, mais le destructeur de Widget n'est pas appelé. Le destructeur du singleton de Meyer est appelé, mais il est difficile voire impossible de savoir quand).

    Ensuite, le choix de l'implémentation dépends du projet sur lequel tu travailles. La question de savoir quelle est la forme la plus facile a définir n'a pas véritablement d'intérêt, puisque la bonne réponse à cette question dépends de l'environnement dans lequel le singleton est intégré.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Membre émérite Avatar de valefor
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    711
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 711
    Par défaut
    Anecdote :

    Avec une vieille implémentation de gcc, sous Solaris, j'ai eu un problème un jour avec ces singletons.

    On faisait un chargement dynamique de bibliothèque.
    De cette bibliothèque on récupérait un singleton.
    On déchargeait la bibliothèque.
    On arrêtait le programme et paf segv (que l'on n'a pas vu dessuite, on a cru pendant longtemps que tout fonctionnait bien).

    Le problème venait que le destructeur du singleton était appelé après déchargement de la lib.

    Avec les versions plus récentes de gcc le problème a été résolu.

  5. #5
    screetch
    Invité(e)
    Par défaut
    en quoi une nouvelle version de gcc resout le probleme ?

  6. #6
    Membre émérite Avatar de valefor
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    711
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 711
    Par défaut
    Elle résolvait le problème au niveau des atexit ou un truc comme cela... l'ordre dans le quel les fonctions de destruction étaient enregistrées tenait compte du déchargement des bibliothèques :
    Avant tout les destructeurs étaient enregistrés à la fin du programme.
    Après, je ne sais plus comment cela marchait mais il n'y avait plus le problème.

  7. #7
    Membre chevronné
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Par défaut
    Bonjour,

    Je trouve vos réponses (Farscape et Emmanuel Deloget) assez mesurées, il me semble que l'on n'a pas vraiment le choix.

    Il me semble que, si l'on veut gérer l'ordre de destruction des singletons, nous sommes "forcés" d'utiliser un
    plutôt qu'un
    ,
    cf la FAQ ici.

    Du coup, ce serait une entorse à la segmentation des responsabilités du code de ne pas gérer la désallocation du pointeur au même endroit que son allocation, c'est à dire dans un objet singleton.

    C'est vrai que si on fait différemment, on n'est pas certain que cela se passe mal : il se peut qu'au cours de la durée de vie du projet, personne n'ajoute jamais aucun autre singleton, ou que les nouveaux singletons ne dépendent jamais des anciens pour leurs construction ou leur destruction.

    Mais cela me semble être du même ordre que pas mal d'autres risques que l'on ne prend pas : il se peut que si l'on n'initialise pas des variables ou si l'on n'alloue pas de la mémoire avant de s'en servir, on n'obtienne jamais de crash ni de fonctionnement aberrant. Et pourtant on ne fait jamais d'entorse à ces règles, même quand on est sur et certain que cela se passera bien.

    Du coup, je me dis qu'il y a quelque chose que j'ai mal compris.
    Pourriez-vous m'éclairer ?
    Merci par avance

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    C'est tout l'avantage de passer par une classe qui contient un pointeur vers l'élément statique

    A partir du moment où ton pointeur est encapsulé, rien ne t'empêche de créer des méthodes qui renverront une référence sur l'instance plutôt qu'un pointeur sur celle-ci...

    En effet, tu peux tout aussi bien bien avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Singleton
    {
        public:
            static Singleton& getInstance()
            {
                 if(!instance)
                     instance= new Singleton;
                 return *instance;
            }
            static void freeInstance()
            {
                delete instance;
                instance = NULL;
            }
        private:
            static Singleton *instance;
    };
    que de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Singleton
    {
        public:
            static Singleton* getInstance()
            {
                 if(!instance)
                     instance= new Singleton;
                 return instance;
            }
            static void freeInstance()
            {
                delete instance;
                instance = NULL;
            }
        private:
            static Singleton *instance;
    };
    et, si ton singleton dépend d'un autre singleton, rien ne t'empêcherait même d'écrire un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Singleton
    {
        public:
            static Singleton& getInstance()
            {
                 /* Singleton2::getInstance() renvoie également une référence
                  * sur l'instance ;)
                  */
     
                 if(!instance)
                     instance= new Singleton(Singleton2::getInstance());
     
                 /* ou, de manière plus lisible: */
                 if(!instance)
                 {
                     Singleton2 s2=Singleton2::getInstance();
                     instance = new Singelton(s2);
                 }
                 /* renvoi de la référence sur l'instance */
                 return *instance;
            }
            static void freeInstance()
            {
                delete instance;
                instance = NULL;
            }
        private:
            static Singleton *instance;
    };
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2005
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 162
    Par défaut
    Et bien merci beaucoup pour vos réponses constructives.

  10. #10
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Pour ma part, et je sais que je vais en surprendre plus d'un, je ne vois pas d'intérêt à vouloir absolument que le singleton soit détruit à un moment quelconque du programme (y compris une fois que main() a fini de s'exécuter). Nous sommes dans un monde simplifié, ou l'OS prends en charge l'unload do process et termine toutes les ressources allouées par ce process. De fait, la mémoire, les fichiers ouverts, etc sont correctement libéré/fermé/etc à la fin du programme.

    Certes, on peut toujours me répondre "ça fait un resource leak ! ". Et bien non: puisque le singleton est censé être valide à partir du moment ou il est instancié et jusqu'à la fin du programme (c'est à dire, dans un sens, jusqu'à ce que le dernier destructeur de la dernière instance qui a une durée de vie statique ait fini de s'exécuter), quel leak est-ce que je crée si je ne le libère pas en quittant ? Aucun, puisque sitôt après l'exécution du destructeur du singleton, on est censé redonner la main à l'OS pour qu'il fasse le clean up.

    En fait, si on donne à l'utilisateur la possibilité de détruire le singleton quand il le souhaite, on prends le risque de l'autoriser à l'utiliser après qu'il ait été détruit - ce qui est bien plus ennuyeux que de ne pas le détruire au niveau de la maintenance du code.

    J'en entends déjà qui me hurlent "mais un utilisateur lambda ne ferais pas ça!". Certes. C'est possible. Le même utilisateur lambda ne déclarera pas deux instances de la même classe si vous lui dites qu'il n'en a pas le droit. Puisque vous vous substituez à son intelligence en interdisant la création d'une seconde instance de votre classe, pourquoi lui permettre de la détruire après ? Ca n'est pas très consistant
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  11. #11
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Pour ma part, et je sais que je vais en surprendre plus d'un, je ne vois pas d'intérêt à vouloir absolument que le singleton soit détruit à un moment quelconque du programme (y compris une fois que main() a fini de s'exécuter). Nous sommes dans un monde simplifié, ou l'OS prends en charge l'unload do process et termine toutes les ressources allouées par ce process. De fait, la mémoire, les fichiers ouverts, etc sont correctement libéré/fermé/etc à la fin du programme.

    Certes, on peut toujours me répondre "ça fait un resource leak ! ". Et bien non: puisque le singleton est censé être valide à partir du moment ou il est instancié et jusqu'à la fin du programme (c'est à dire, dans un sens, jusqu'à ce que le dernier destructeur de la dernière instance qui a une durée de vie statique ait fini de s'exécuter), quel leak est-ce que je crée si je ne le libère pas en quittant ? Aucun, puisque sitôt après l'exécution du destructeur du singleton, on est censé redonner la main à l'OS pour qu'il fasse le clean up.

    En fait, si on donne à l'utilisateur la possibilité de détruire le singleton quand il le souhaite, on prends le risque de l'autoriser à l'utiliser après qu'il ait été détruit - ce qui est bien plus ennuyeux que de ne pas le détruire au niveau de la maintenance du code.

    J'en entends déjà qui me hurlent "mais un utilisateur lambda ne ferais pas ça!". Certes. C'est possible. Le même utilisateur lambda ne déclarera pas deux instances de la même classe si vous lui dites qu'il n'en a pas le droit. Puisque vous vous substituez à son intelligence en interdisant la création d'une seconde instance de votre classe, pourquoi lui permettre de la détruire après ? Ca n'est pas très consistant
    Je me ralie sans difficulté à ton point de vue, et je le prendrai en considération dans le futur.

    Cependant, je me dis qu'il faut peut être tempérer un peu le raisonnement en fonction du but recherché par le singleton...

    En effet, dans quelle mesure la destruction (et la "re-création") du singleton n'est elle pas la possibilité de s'assurer d'une libération correcte des ressources utilisées par celui-ci afin de permettre de repartir sur "une feuille blanche" dans certains cas particuliers

    Je pense entre autre, et même si je prévois que tu va hurler à la lecture de ceci, à un singleton dont l'objectif serait de gérer des ressources différentes selon les "niveaux" ou les "cartes" dans un jeu en présentant plusieurs, avec des sons, des images et des représentations graphiques différentes...

    Mais je crois que la problématique vient plus au fait qu'il faille s'assurer de l'existence d'une instance cohérente , que l'on veut unique, et de s'assurer de pouvoir la récupérer de manière sécurisée que du fait que l'on permette (ou non) à l'utilisateur de la libérer...
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  12. #12
    Membre chevronné
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Par défaut
    Rebonjour à tous,

    Citation Envoyé par Emmanuel Deloget Voir le message
    Pour ma part, et je sais que je vais en surprendre plus d'un, je ne vois pas d'intérêt à vouloir absolument que le singleton soit détruit à un moment quelconque du programme (y compris une fois que main() a fini de s'exécuter).
    Intéressant... J'avoue ne pas savoir quoi en penser.

    Je suis bien d'accord avec toi quand tu dis
    Citation Envoyé par Emmanuel Deloget Voir le message
    En fait, si on donne à l'utilisateur la possibilité de détruire le singleton quand il le souhaite, on prends le risque de l'autoriser à l'utiliser après qu'il ait été détruit - ce qui est bien plus ennuyeux que de ne pas le détruire au niveau de la maintenance du code.
    En même temps, je suis aussi convaincu du fait que si l'on veut détruire les Singletons, il faut le faire à la main. Faute de quoi on prend le risque de ne pas les détruire dans le bon ordre.

    Du coup, je suis perplexe quant à ta proposition de ne pas les détruire.

    Quant à la remarque suivante :
    Citation Envoyé par koala01
    Je pense entre autre, et même si je prévois que tu va hurler à la lecture de ceci, à un singleton dont l'objectif serait de gérer des ressources différentes selon les "niveaux" ou les "cartes" dans un jeu en présentant plusieurs, avec des sons, des images et des représentations graphiques différentes...
    Et bien je hurle en effet. On en le répètera jamais assez : minimisons l'utilisation des singletons. Les singletons c'est pratique à coder, mais c'est super-pénalisant quand on veut faire évoluer son programme. Qui plus est, cela cache souvent un défaut de conception.
    Il suffit d'utiliser un singleton quelque part pour se rendre compte qu'on avait en fait besoin de plusieurs objets (ou qu'on aurait pu avoir besoin de plusieurs objets). Le cas cité : gérer des jeux de ressources différents, en est un exemple.

    Bonne journée.

  13. #13
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Sur le point de la destruction et la création, j'ai dans mon projet personel quelques objets singleton qui ne sont créés puis détruit que sur demande.
    Je prends par exemple le système graphique : il ne doit pas être créé immédiatement parce-que plusieurs traitements doivent être effectués avant sa création. De même, je dois pouvoir le détruire complètement (au sens : pas seulement le dés-initialiser, mais bien le détruire) et le recréer par la suite sans avoir à relancer mon application.
    J'ai quelques systèmes qui marchent comme ça et qui doivent toute de même être singleton pour profiter des propriétés citées au dessus. J'ai aussi des singletons qui n'ont pas besoin d'être potentiellement détruit sur demande, mais ils sont rare.

    Comme quoi, ce n'est pas toujours interessant d'avoir un singleton "durant tout le temps de l'application", même si c'est en grande partie vrai.

    Une note a part : vivement que les mecs de boosts se mettent d'acord sur la lib de singleton...

  14. #14
    Alp
    Alp est déconnecté
    Expert confirmé

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Par défaut
    Le singleton est un sujet récurrent sur ce forum.
    Une petite recherche avancée sur le mot singleton dans le forum C++ donne déjà pas mal d'éléments de réponse, en particulier par rapport aux interventions de spécialistes

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Servlet]Singleton & cache
    Par lucimast dans le forum Servlets/JSP
    Réponses: 4
    Dernier message: 15/12/2004, 16h36
  2. Singleton héritable ?
    Par rolkA dans le forum C++
    Réponses: 10
    Dernier message: 11/12/2004, 16h22
  3. [Débutant] pattern singleton
    Par SirDarken dans le forum Débuter avec Java
    Réponses: 22
    Dernier message: 11/12/2004, 01h55
  4. Mutiple row in singleton select ????? [Important, merci]
    Par SkyDev dans le forum Bases de données
    Réponses: 6
    Dernier message: 20/04/2004, 14h02
  5. [debutant]Singleton
    Par bafman dans le forum Langage SQL
    Réponses: 6
    Dernier message: 13/01/2004, 15h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo