Question Modèle de conception Singleton C ++


Récemment, je suis tombé sur une réalisation / mise en œuvre du modèle de conception Singleton pour C ++. Il a ressemblé à ceci (je l'ai adopté de l'exemple de la vie réelle):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

De cette déclaration je peux déduire que le champ d'instance est initié sur le tas. Cela signifie qu'il y a une allocation de mémoire. Ce qui est complètement flou pour moi, c'est quand exactement la mémoire va être désallouée? Ou y a-t-il une fuite de bogue et de mémoire? Il semble qu'il y ait un problème dans la mise en œuvre.

Ma question principale est, comment puis-je l'implémenter correctement?


545
2018-06-17 16:02


origine


Réponses:


Voir cet article pour une conception simple pour un paresseux évalué avec destruction garantie singleton:
Quelqu'un peut-il me fournir un échantillon de Singleton en c ++? 

Le classique paresseux évalué et correctement détruit singleton.

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are unacceptable otherwise you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

Voir cet article sur quand utiliser un singleton: (pas souvent)
Singleton: Comment devrait-il être utilisé

Voir cet article sur l'ordre d'initialisation et comment faire face:
Ordre d'initialisation des variables statiques
Recherche de problèmes d'ordre d'initialisation statique C ++ 

Voir cet article décrivant les durées de vie:
Quelle est la durée de vie d'une variable statique dans une fonction C ++? 

Consultez cet article qui traite de certaines implications de threading pour les singletons:
Instance Singleton déclarée comme variable statique de la méthode GetInstance

Voir cet article qui explique pourquoi le double contrôle ne fonctionnera pas sur C ++:
Quels sont tous les comportements communs non définis qu'un programmeur C ++ devrait connaître?
Dr Dobbs: C ++ et les dangers du double verrouillage: partie I


841
2018-06-17 16:49



Étant un Singleton, vous ne voulez généralement pas qu'il soit détruit.

Il sera détruit et libéré lorsque le programme se termine, ce qui est le comportement normal souhaité pour un singleton. Si vous voulez pouvoir le nettoyer explicitement, il est assez facile d'ajouter une méthode statique à la classe qui vous permet de le restaurer dans un état propre, et de le réaffecter la prochaine fois qu'il est utilisé, mais cela sort du cadre d'un Singleton "classique".


36
2018-06-17 16:06



Vous pourriez éviter l'allocation de mémoire. Il existe de nombreuses variantes, toutes ayant des problèmes en cas d'environnement multithread.

Je préfère ce genre d'implémentation (en fait, on ne dit pas correctement que je préfère, car j'évite autant que possible les singletons):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

Il n'a pas d'allocation de mémoire dynamique.


27
2018-06-17 16:10



Une autre alternative non-allocation: créer un singleton, par exemple de classe C, comme vous en avez besoin:

singleton<C>()

en utilisant

template <class X>
X& singleton()
{
    static X x;
    return x;
}

Ni cela ni la réponse de Cătălin ne sont automatiquement thread-safe dans le C ++ actuel, mais seront en C ++ 0x.


10
2018-06-17 16:15



La réponse de @Loki Astari est excellente.

Cependant, il y a des fois avec plusieurs objets statiques où vous devez être en mesure de garantir que le singleton ne sera pas détruit jusqu'à ce que tous vos objets statiques qui utilisent le singleton n'en a plus besoin.

Dans ce cas std::shared_ptr peut être utilisé pour garder le singleton vivant pour tous les utilisateurs même lorsque les destructeurs statiques sont appelés à la fin du programme:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};

8
2017-10-31 06:56



Si vous voulez allouer l'objet dans le tas, pourquoi ne pas utiliser un pointeur unique. La mémoire sera également désallouée puisque nous utilisons un pointeur unique.

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

5
2018-01-11 21:11



La solution dans la réponse acceptée a un inconvénient significatif - le destructeur pour le singleton est appelé après que le contrôle quitte la fonction "principale". Il peut y avoir des problèmes quand certains objets dépendants sont alloués dans "main".

J'ai rencontré ce problème, en essayant d'introduire un Singleton dans l'application Qt. J'ai décidé, que toutes mes boîtes de dialogue d'installation doivent être Singletons, et a adopté le modèle ci-dessus. Malheureusement, la classe principale "QApplication" de Qt a été allouée sur la pile dans la fonction "main", et Qt interdit de créer / détruire des dialogues quand aucun objet d'application n'est disponible.

C'est pourquoi je préfère les singletons alloués par tas. Je fournis une méthode explicite "init ()" et "term ()" pour tous les singletons et les appelle dans "main". Ainsi, j'ai un contrôle total sur l'ordre de création / destruction des singletons, et je garantis aussi que les singletons seront créés, peu importe si quelqu'un a appelé "getInstance ()" ou non.


4
2018-06-18 08:25