Partage
  • Partager sur Facebook
  • Partager sur Twitter

Difficultés avec une classe singleton

les méthodes statiques

Sujet résolu
    5 juillet 2007 à 20:00:41

    Bonjour,
    J'essaye de recréer une classe singleton à l'aide du tuto de Davidbrcz. J'ai pu réaliser le code suivant:

    BasicSingleton.h
    #ifndef FILE_H
    #define FILE_H

    template <typename T>
    class BasicSingleton
    {
    public:
        static T *GetInstance(); // renvoie l'objet singleton
        static void Terminate(); // libère l'objet singleton

    protected:
        static T *m_singleton;
        BasicSingleton();
        ~BasicSingleton();
    };

    #endif
     

    BasicSingleton.cpp
    #ifndef FILE_CPP
    #define FILE_CPP

    #include <iostream>
    #include "file.h"

    using namespace std;

    template <typename T> T *BasicSingleton<T>::m_singleton = 0;

    template <typename T> BasicSingleton<T>::BasicSingleton()
    {
        cout << "singleton created" << endl;
    }

    template <typename T> BasicSingleton<T>::~BasicSingleton()
    {
        cout << "singleton destroyed" << endl;
    }

    template <typename T> T *BasicSingleton<T>::GetInstance()
    {
        if(m_singleton == 0)
        {
            m_singleton = new T;
        }

        return m_singleton;
    }

    template <typename T> void BasicSingleton<T>::Terminate()
    {
        if(m_singleton != 0)
        {
            delete m_singleton;
        }
    }

    #endif
     

    main.cpp
    #include <iostream>
    #include "file.h"
    #include "CSingleton.h"

    using namespace std;

    class Test : public BasicSingleton<Test> // classe singleton fille
    {
        Test()
        {
            cout<<"Test::Test()"<<endl;
        }

        ~Test()
        {
            cout<<"Test::~Test()"<<endl;
        }
    };

    int main(int argc, char **argv)
    {
        Test *test = BasicSingleton<Test>::GetInstance(); // appelle Test::Test()
        Test::Terminate(); // appelle Test::~Test()
        return 0;
    }
     


    Voilà. Je n'est pas d'erreur mais le log du compilo indique:

    Citation : CodeBlock

    .objs\main.o:main.cpp:(.text+0x14f): undefined reference to `BasicSingleton<Test>::GetInstance()'
    .objs\main.o:main.cpp:(.text+0x157): undefined reference to `BasicSingleton<Test>::Terminate()'
    collect2: ld returned 1 exit status
    Process terminated with status 1 (0 minutes, 3 seconds)
    0 errors, 0 warnings

    Ce sont les deux méthodes statiques qui posent problème.
    Merci d'avance pour vos réponses.
    • Partager sur Facebook
    • Partager sur Twitter
      5 juillet 2007 à 20:52:46

      Les fonctions membres d'une classe template doivent être implémentées dans le .h ;)
      Ou autrement tu inclues le .cpp...
      • Partager sur Facebook
      • Partager sur Twitter
      Anonyme
        5 juillet 2007 à 20:59:49

        Se ne sont pas les méthode statique qui pose un problème mais le fait que tu sépare déclaration/définition dans deux fichiers différent(un .h et un .cpp).

        Sa solution est ici : http://c.developpez.com/faq/cpp/?page=templates#DIVERS_templates


        Et en plus je viens de me rendre compte que mon tuto comportait une erreur assez grave .
        Comment j'ai fait pour la louper !!
        Je la corrige ici en attendant de le faire sur le tuto :
        La classe dérivé devient:

        class Test : public BasicSingleton<Test> // classe singleton fille
        {

        friend class BasicSingleton<Test>; //pour que le singleton est accez au constructeur/destructeur

            Test()
            {
                cout<<"Test::Test()"<<endl;
            }

            ~Test()
            {
                cout<<"Test::~Test()"<<endl;
            }
        };

         
        • Partager sur Facebook
        • Partager sur Twitter
          6 juillet 2007 à 14:50:44

          Pourquoi a-t-il besoin d'être ami si c'est déjà un classe héritée?

          Pour le reste tu peux ajouter un compteur de références à ton singleton car imagine, tu appel deux fois ton GetInstance (disons dans 2 threads différents) il y en a un qui se termine : le singleton sera automatiquement supprimé...

          alors que si tu as un compteur qui incrémente à chaque demande d'instance c'est sécuritaire
          template <typename T> T *BasicSingleton<T>::GetInstance()
          {
              if(m_singleton == 0)
              {
                  m_singleton = new T;
              }

              compteur++;
              return m_singleton;
          }

          Instance 1 : allocation -> compteur = 1;
          Instance 2 : compteur = 2;
          template <typename T> void BasicSingleton<T>::Terminate()
          {
              if( m_singleton != 0 && --compteur == 0 )
              {
                  delete m_singleton;
                  m_singleton = 0; // n'oublie pas de remettre ton pointeur à NULL si tu veux avoir une chance de pouvoir le réinitialisé
              }
          }

          Instance 1 : compteur = 1
          Instance 2 : compteur = 0 -> destruction

          De plus (mais là je fais du zèle) tu peux mettre le Terminate dans le destructeur de ta classe test. Comme ça tu ne peux pas oublié ton singleton en mémoire.

          P.S. n'oublie pas de gérer les exceptions d'allocations c'est primordial dans ce genre de concept... Imagine un singleton nécessaire à 30 endroits différents qui n'arrive pas à s'initialiser!! http://c.developpez.com/faq/cpp/?page=pointeurs#POINTEURS_new_erreur
          • Partager sur Facebook
          • Partager sur Twitter
          Anonyme
            6 juillet 2007 à 15:58:40

            Citation : Pas de titre

            Pourquoi a-t-il besoin d'être ami si c'est déjà un classe héritée?

            Car sinon le singleton ne peut pas appeler le constructeur privé de classe dérivée.

            Apres le code sur le comptage de reférence est bon mais il pourrait être légrement amélioré en mettant le compteur de refrence dans un objet composé de la clase singleton.


            Citation : Pas de titre


            De plus (mais là je fais du zèle) tu peux mettre le Terminate dans le destructeur de ta classe test. Comme ça tu ne peux pas oublié ton singleton en mémoire.



            Je ne vois pas a quoi cela servirait puisque le destructeur est inapelable de l'exterieur de la classe .Faire un delete dessue impossible.
            Et mettre Terminate dans le destructeur appellerai Terminate->delete->Terminate.
            Or il y aurait apres execution de la 2eme fonction Terminate , un retour d'appel vers un endroit qui n'existe plus --> segfault.


            • Partager sur Facebook
            • Partager sur Twitter
              6 juillet 2007 à 17:14:32

              Si, je n'avais pas remarqué que les constructeurs/destructeur de la classe fille était private.

              Et pour le terminate dans le desctructeur... Je me suis mélanger avec un Gestionnaire de singleton comme disais mon prof de client/Serveur... (Je dis "disais"... j'ai gradué ;) )

              En passant voici un lien sur sa page, il a une technique beaucoup plus simple pour gérer les singletons sans pointeurs

              http://h-deb.clg.qc.ca/Sujets/Divers--cplusplus/CPP--Singletons.html
              • Partager sur Facebook
              • Partager sur Twitter
                6 juillet 2007 à 18:50:37

                Je vous remercie de toutes vos réponses et de vos liens qui viendront enrichir mes favoris. j'ai compris que le problème vient que je déclarais et implémentais mes méthodes templates dans un fichier différent. J'ai donc copié-collé les méthodes du fichier 'BasicSingleton.cpp', faute faire un fichier 'BasicSingleton.tpp' à inclure à la fin de 'BasicSingleton.h'.
                J'ai le code suivant:

                BasicSingleton.h
                #include <cstdlib>
                #include <iostream>

                using namespace std;

                template <typename T>
                class BasicSingleton
                {
                public:
                    static T *GetInstance() // renvoie l'objet singleton
                    {
                        if(m_singleton == NULL)
                        {
                            m_singleton = new T;
                        }

                        return m_singleton;
                    }

                    static void Terminate() // libère l'objet singleton
                    {
                        if(m_singleton != NULL)
                        {
                            delete m_singleton;
                        }
                    }

                protected:
                    static T *m_singleton;

                    BasicSingleton()
                    {
                        cout << "singleton created" << endl;
                    }

                    ~BasicSingleton()
                    {
                        cout << "singleton destroyed" << endl;
                    }
                };

                template <typename T> T *BasicSingleton<T>::m_singleton = 0;

                #endif
                 

                main.cpp
                #include <iostream>
                #include "file.h"
                #include "CSingleton.h"

                using namespace std;

                class Test : public BasicSingleton<Test> // classe singleton fille
                {
                    friend class BasicSingleton<Test>;

                    Test()
                    {
                        cout<<"Test::Test()"<<endl;
                    }

                    ~Test()
                    {
                        cout<<"Test::~Test()"<<endl;
                    }
                };

                int main(int argc, char **argv)
                {
                    Test *test = BasicSingleton<Test>::GetInstance(); // affiche : 'singleton created'
                    Test::Terminate(); // affiche : 'singleton destroyed'
                    return 0;
                }
                 

                Le lien de MatteX est aussi trés interressant. La dernière méthode est tout à fait adapté pour flemmard comme moi :p .
                • Partager sur Facebook
                • Partager sur Twitter
                  6 juillet 2007 à 23:57:26

                  Citation : MatteX

                  En passant voici un lien sur sa page, il a une technique beaucoup plus simple pour gérer les singletons sans pointeurs

                  http://h-deb.clg.qc.ca/Sujets/Divers--cplusplus/CPP--Singletons.html


                  J'imagine que tu fais allusion à la méthode popularisée par Scott Meyers avec la variable locale statique.

                  Cette approche suffit amplement :
                  - en l'absence de besoin de destruction et de tentantives de construction concurrentes (depuis des threads différents). En fait, aucune technique existante ne marche en situation de construction dans un environnement MT. Autant construire la variable globale (oui, un singleton n'est qu'une variable déguisée) depuis le thread principal lors d'une phase d'initialisation, et l'utiliser depuis les autres threads.
                  - s'il n'y a pas besoin d'établir des dépendences pour détruire des singletons
                  - si on ne veut pas construire le singleton avec des paramètres

                  De l'excellente lecture sur le sujet : un chapitre de Modern C++ Design (MC++D) d'Andrei Alexandrecu, publié chez Addisson Welsey.


                  PS: Tant que j'y suis: http://c.developpez.com/faq/cpp/?page=pointeurs#POINTEURS_delete_NULL

                  PPS: Vu la nature profonde d'un singleton (i.e.: une variable globale), on peut aussi faire simple et se contenter d'une approche non intrusive. Après, ce n'est que convention d'écriture. (Loki (la bibliothèque de MC++D), et ACE (le framework enclume Réseau&MT) ont adopté ce type d'approche).
                  template <typename T> struct Singleton {
                      static T& instance() {
                         assert(s_instance && "Use create first!");
                         return *s_instance;
                      }
                      static void create() {
                          assert(!s_instance && "Don't use create twice!");
                          s_instance = new T();
                      }
                  private:
                      static T * s_instance;
                  }

                  template <typename T> Singleton<T>::s_instance = 0;

                  typedef Singleton<Toto> TOTOMANAGER;

                  int main() {
                      ...
                      TOTOMANAGER::create();
                      ......
                      TOTOMANAGER::instance().titi();
                   
                  • Partager sur Facebook
                  • Partager sur Twitter
                  C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.

                  Difficultés avec une classe singleton

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                  • Editeur
                  • Markdown