Modèle de conception C ++ Singleton


737

Récemment, je suis tombé sur une réalisation / implémentation du modèle de conception Singleton pour C ++. Cela ressemble à ceci (je l'ai adopté à partir 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 n'est pas clair pour moi, c'est quand exactement la mémoire va être désallouée? Ou y a-t-il un bug et une fuite de mémoire? Il semble qu'il y ait un problème dans la mise en œuvre.

Ma principale question est, comment puis-je l'implémenter de la bonne manière?



10
Vous trouverez une excellente discussion sur la façon d'implémenter un singleton, ainsi que la sécurité des threads en C ++ dans cet article. aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf

106
@sbi - Seul un Sith traite en absolu. La grande majorité des problèmes peuvent-ils être résolus sans Singletons? Absolument. Les singletons causent-ils leurs propres problèmes? Oui. Cependant, je ne peux honnêtement pas dire qu'ils sont mauvais , car la conception consiste à considérer les compromis et à comprendre les nuances de votre approche.
derekerdmann

11
@derekerdmann: Je n'ai pas dit que vous n'avez jamais besoin d'une variable globale (et quand vous en avez besoin, un Singleton est parfois mieux). Ce que j'ai dit, c'est qu'ils devraient être utilisés le moins possible. Glorifier Singleton en tant que modèle de conception précieux donne l'impression qu'il est bon de l'utiliser, plutôt que d'être un hack , ce qui rend le code difficile à comprendre, difficile à maintenir et difficile à tester. C'est pourquoi j'ai posté mon commentaire. Jusqu'à présent, rien de ce que vous avez dit ne contredit cela.
sbi

13
@sbi: Ce que vous avez dit était "Ne les utilisez pas". Pas beaucoup plus raisonnable "ils devraient être utilisés le moins possible", vous avez changé plus tard - vous voyez sûrement la différence.
2011

Réponses:


1107

En 2008, j'ai fourni une implémentation C ++ 98 du modèle de conception Singleton qui est évaluée paresseusement, à destruction garantie et non techniquement sécurisée pour les threads:
quelqu'un peut-il me fournir un échantillon de Singleton en c ++?

Voici une implémentation C ++ 11 mise à jour du modèle de conception Singleton qui est évaluée paresseusement, correctement détruite et thread-safe .

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
};

Consultez cet article pour savoir quand utiliser un singleton: (pas souvent)
Singleton: comment doit-il être utilisé

Consultez ces deux articles sur l'ordre d'initialisation et comment y faire face:
Ordre d'initialisation des variables statiques
Recherche des problèmes d'ordre d'initialisation statique C ++

Consultez 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: l'
instance Singleton déclarée comme variable statique de la méthode GetInstance, est-elle thread-safe?

Consultez cet article qui explique pourquoi le verrouillage à double vérification ne fonctionnera pas sur C ++:
Quels sont tous les comportements non définis courants qu'un programmeur C ++ devrait connaître?
Dr Dobbs: C ++ et les dangers du verrouillage à double vérification: première partie


23
Bonne réponse. Mais il faut noter que ce n'est pas threadover
Varuna

4
@zourtney: Beaucoup de gens ne réalisent pas ce que vous venez de faire :)
Johann Gerell

4
@MaximYegorushkin: Quand cela est détruit, c'est très bien défini (il n'y a pas d'ambiguïté). Voir: stackoverflow.com/questions/246564/…
Martin York

3
What irks me most though is the run-time check of the hidden boolean in getInstance()C'est une hypothèse sur la technique de mise en œuvre. Il n'est pas nécessaire de supposer qu'il est vivant. voir stackoverflow.com/a/335746/14065 Vous pouvez forcer une situation afin qu'elle soit toujours vivante (moins de frais généraux queSchwarz counter ). Les variables globales ont plus de problèmes avec l'ordre d'initialisation (entre les unités de compilation) car vous ne forcez pas un ordre. L'avantage de ce modèle est 1) l'initialisation paresseuse. 2) Capacité à exécuter une ordonnance (Schwarz aide mais est plus laid). Oui, get_instance()c'est beaucoup plus laid.
Martin York

3
@kol: Non. Ce n'est pas l'habituel. Ce n'est pas parce que les débutants copient et collent du code sans réfléchir que cela est normal. Vous devez toujours regarder le cas d'utilisation et vous assurer que l'opérateur d'affectation fait ce qui est attendu. Copier et coller du code va vous conduire à des erreurs.
Martin York

47

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

Il sera démoli et désalloué à la fin du programme, ce qui est le comportement normal et 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 la restaurer dans un état propre et de la réallouer la prochaine fois qu'elle est utilisée, mais cela sort du cadre d'un singleton "classique".


4
si la suppression n'est jamais explicitement appelée sur l'instance statique de Singleton *, cela ne serait-il pas techniquement considéré comme une fuite de mémoire?
Andrew Garrison

7
Ce n'est plus une fuite de mémoire qu'une simple déclaration d'une variable globale.
ilya n.

15
Pour mettre les choses au clair ... les problèmes de "fuite de mémoire" vis-à-vis des singletons sont complètement hors de propos. Si vous avez des ressources avec état dans lesquelles l'ordre de déconstruction est important, les singletons peuvent être dangereux; mais toute la mémoire est proprement récupérée par le système d'exploitation à la fin du programme ... annulant ce point totalement académique dans 99,9% des cas. Si vous voulez discuter la grammaire d'avant en arrière de ce qui est et n'est pas une "fuite de mémoire", c'est bien, mais réalisez que c'est une distraction des décisions de conception réelles.
jkerian

12
@jkerian: Les fuites et destructions de mémoire dans le contexte C ++ ne concernent pas vraiment les fuites de mémoire. Il s'agit vraiment de contrôler les ressources. Si vous perdez de la mémoire, le destructeur n'est pas appelé et les ressources associées à l'objet ne sont donc pas correctement libérées. La mémoire n'est qu'un exemple simple que nous utilisons pour enseigner la programmation, mais il existe des ressources beaucoup plus complexes.
Martin York

7
@Martin, je suis entièrement d'accord avec vous. Même si la seule ressource est la mémoire, vous aurez toujours des problèmes pour essayer de trouver de vraies fuites dans votre programme si vous devez parcourir une liste de fuites, en filtrant celles qui "n'ont pas d'importance". Il est préférable de tout nettoyer afin que tout outil signalant des fuites ne signale que les choses QUI SONT un problème.
Dolphin

38

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

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

class Singleton
{
private:
   Singleton();

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

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


3
Dans certains cas, cette initialisation paresseuse n'est pas le modèle idéal à suivre. Par exemple, si le constructeur du singleton alloue de la mémoire à partir du tas et que vous souhaitez que cette allocation soit prévisible, par exemple dans un système embarqué ou un autre environnement étroitement contrôlé. Je préfère, lorsque le modèle Singleton est le meilleur modèle à utiliser, pour créer l'instance en tant que membre statique de la classe.
dma

3
Pour de nombreux programmes plus importants, en particulier ceux avec des bibliothèques dynamiques. Tout objet global ou statique qui n'est pas primitif peut entraîner des erreurs de segmentation / plantages à la sortie du programme sur de nombreuses plates-formes en raison de problèmes d'ordre de destruction lors du déchargement des bibliothèques. C'est l'une des raisons pour lesquelles de nombreuses conventions de codage (y compris celles de Google) interdisent l'utilisation d'objets statiques et globaux non triviaux.
obecalp

Il semble que l'instance statique dans une telle implémentation ait un lien interne et aura des copies uniques et indépendantes dans différentes unités de traduction, ce qui entraînera un comportement déroutant et incorrect. Mais j'ai vu beaucoup de telles implémentations, ai-je raté quelque chose?
FaceBro

Qu'est-ce qui empêche l'utilisateur d'assigner cela à plusieurs objets lorsque le compilateur en arrière-plan utilise son propre constructeur de copie?
Tony Tannous

19

@Loki Astari's answer est excellente.

Cependant, il y a des moments 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 plus besoin.

Dans ce cas, std::shared_ptrpeut être utilisé pour maintenir 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() {}
};

9

Une autre alternative sans allocation: créez un singleton, disons de classe C, comme vous en avez besoin:

singleton<C>()

en utilisant

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

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


Actuellement sous gcc, il est thread-safe (et ce depuis un certain temps).
Martin York

13
Le problème avec cette conception est que s'il est utilisé sur plusieurs bibliothèques. Chaque bibliothèque possède sa propre copie du singleton que cette bibliothèque utilise. Ce n'est donc plus un singleton.
Martin York

6

Je n'ai pas trouvé d'implémentation CRTP parmi les réponses, alors voici:

template<typename HeirT>
class Singleton
{
public:
    Singleton() = delete;

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;

    static HeirT &instance()
    {
        static HeirT instance;
        return instance;
    }
};

Pour utiliser, héritez simplement de votre classe, comme: class Test : public Singleton<Test>


1
Impossible de faire fonctionner cela avec C ++ 17 tant que je n'ai pas protégé le constructeur par défaut et '= default;'.
WFranczyk

6

La solution de la réponse acceptée présente un inconvénient important: le destructeur du singleton est appelé une fois que le contrôle a quitté la main()fonction. Il peut y avoir vraiment des problèmes lorsque certains objets dépendants sont alloués à l'intérieurmain .

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 de configuration devaient être des singletons et j'ai adopté le modèle ci-dessus. Malheureusement, la classe principale de Qt a QApplicationété allouée sur pile dans la mainfonction, et Qt interdit de créer / détruire des boîtes de dialogue quand aucun objet d'application n'est disponible.

C'est pourquoi je préfère les singletons alloués en tas. Je fournis une explicite init()et des term()méthodes pour tous les singletons et les appelle à l'intérieur main. Ainsi, j'ai un contrôle total sur l'ordre de création / destruction des singletons, et je garantis également que les singletons seront créés, que quelqu'un l'appelle getInstance()ou non.


2
Si vous faites référence à la réponse actuellement acceptée, votre première affirmation est fausse. Le destructeur n'est appelé que lorsque tous les objets de durée de stockage statique sont détruits.
Martin York,

5

Voici une mise en œuvre facile.

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

Un seul objet créé et cette référence d'objet est renvoyée à chaque fois par la suite.

SingletonClass instance created!
00915CB8
00915CB8

Ici 00915CB8 est l'emplacement mémoire de l'objet singleton, identique pour la durée du programme mais (normalement!) Différent à chaque exécution du programme.

NB Ce n'est pas un fil sûr. Vous devez assurer la sécurité du fil.


5

Si vous souhaitez allouer l'objet en tas, pourquoi ne pas utiliser un pointeur unique. La mémoire sera également désallouée car 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);

3
Déconseillé en c ++ 11. unique_ptr est recommandé à la place. cplusplus.com/reference/memory/auto_ptr cplusplus.com/reference/memory/unique_ptr
Andrew

2
Ce n'est pas sûr pour les threads. Mieux vaut faire m_sun local staticde getInstance()et l'initialiser immédiatement sans test.
Galik

2

Il est en effet probablement attribué à partir du tas, mais sans les sources, il n'y a aucun moyen de le savoir.

L'implémentation typique (tirée d'un code que j'ai déjà dans emacs) serait:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

... et comptez sur le programme hors de portée pour le nettoyer ensuite.

Si vous travaillez sur une plate-forme où le nettoyage doit être effectué manuellement, j'ajouterais probablement une routine de nettoyage manuel.

Un autre problème avec cette méthode est qu'elle n'est pas adaptée aux threads. Dans un environnement multithread, deux threads pourraient passer par le "si" avant que l'un ou l'autre n'ait la possibilité d'allouer la nouvelle instance (donc les deux le feraient). Ce n'est toujours pas trop grave si vous comptez sur la fin du programme pour nettoyer de toute façon.


vous pouvez en déduire, car vous pouvez voir que la variable d'instance est un pointeur vers l'instance de classe.
Artem Barger

3
Il n'est pas nécessaire d'allouer dynamiquement le singleton. En fait, c'est une mauvaise idée car il n'y a aucun moyen de désallouer automatiquement en utilisant la conception ci-dessus. Laissez-le tomber hors de portée n'est pas appeler des destructeurs et est juste paresseux.
Martin York

Vous pouvez automatiquement désallouer en utilisant la fonction atexit. C'est ce que nous faisons (sans dire que c'est une bonne idée)
Joe

2

Quelqu'un a-t-il mentionné std::call_onceet std::once_flag? La plupart des autres approches - y compris le verrouillage à double vérification - sont rompues.

Un problème majeur dans l'implémentation d'un modèle singleton est l'initialisation sûre. Le seul moyen sûr est de protéger la séquence d'initialisation avec des barrières de synchronisation. Mais ces obstacles eux-mêmes doivent être mis en place en toute sécurité. std::once_flagest le mécanisme pour obtenir une initialisation sûre et garantie.


2

Nous avons récemment abordé ce sujet dans ma classe EECS. Si vous voulez regarder les notes de cours en détail, visitez http://umich.edu/~eecs381/lecture/IdiomsDesPattsCreational.pdf

Il y a deux façons que je connais de créer correctement une classe Singleton.

Première voie:

Implémentez-le de la même manière que vous l'avez dans votre exemple. Quant à la destruction, «les singletons durent généralement pendant la durée de l'exécution du programme; la plupart des systèmes d'exploitation récupèrent la mémoire et la plupart des autres ressources à la fin d'un programme, il y a donc un argument pour ne pas s'inquiéter de cela».

Cependant, il est recommandé de nettoyer à la fin du programme. Par conséquent, vous pouvez le faire avec une classe SingletonDestructor statique auxiliaire et le déclarer comme ami dans votre Singleton.

class Singleton {
public:
  static Singleton* get_instance();

  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

Le Singleton_destroyer sera créé au démarrage du programme et "lorsque le programme se termine, tous les objets globaux / statiques sont détruits par le code d'arrêt de la bibliothèque d'exécution (inséré par l'éditeur de liens), donc le_destroyer sera détruit; son destructeur supprimera le Singleton, exécutant son destructeur. "

Deuxième voie

C'est ce qu'on appelle le Meyers Singleton, créé par l'assistant C ++ Scott Meyers. Définissez simplement get_instance () différemment. Maintenant, vous pouvez également vous débarrasser de la variable membre du pointeur.

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

C'est bien car la valeur retournée est par référence et vous pouvez utiliser la .syntaxe au lieu d' ->accéder aux variables membres.

"Le compilateur crée automatiquement du code qui crée 's' pour la première fois via la déclaration, pas par la suite, puis supprime l'objet statique à la fin du programme."

Notez également qu'avec le Meyers Singleton, vous "pouvez vous retrouver dans une situation très difficile si les objets dépendent les uns des autres au moment de la terminaison - quand le Singleton disparaît-il par rapport à d'autres objets? Mais pour les applications simples, cela fonctionne très bien".


1

En plus de l'autre discussion ici, il peut être intéressant de noter que vous pouvez avoir une globalité, sans limiter l'utilisation à une seule instance. Par exemple, considérons le cas d'une référence comptant quelque chose ...

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

Maintenant, quelque part dans une fonction (comme main), vous pouvez faire:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

Les arbitres n'ont pas besoin de stocker un pointeur sur leurs respectifs Storecar ces informations sont fournies au moment de la compilation. Vous n'avez pas non plus à vous soucier de la Storedurée de vie du car le compilateur requiert qu'il soit global. S'il n'y a en effet qu'une seule instance, Storeil n'y a pas de frais généraux dans cette approche; avec plus d'une instance, c'est au compilateur d'être intelligent sur la génération de code. Si nécessaire, la ItemRefclasse peut même être un frienddesStore (vous pouvez avoir des amis templated!).

Si Storeelle-même est une classe basée sur des modèles, les choses deviennent plus compliquées, mais il est toujours possible d'utiliser cette méthode, peut-être en implémentant une classe d'assistance avec la signature suivante:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

L'utilisateur peut désormais créer un StoreWrappertype (et une instance globale) pour chaque Storeinstance globale , et toujours accéder aux magasins via leur instance d'encapsuleur (oubliant ainsi les détails sanglants des paramètres de modèle nécessaires à l'utilisation Store).


0

Il s'agit de la gestion de la durée de vie des objets. Supposons que vous ayez plus que des singletons dans votre logiciel. Et ils dépendent de Logger singleton. Pendant la destruction de l'application, supposons qu'un autre objet singleton utilise Logger pour consigner ses étapes de destruction. Vous devez garantir que l'enregistreur doit être nettoyé en dernier. Par conséquent, veuillez également consulter ce document: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


0

Mon implémentation est similaire à celle de Galik. La différence est que mon implémentation permet aux pointeurs partagés de nettoyer la mémoire allouée, au lieu de conserver la mémoire jusqu'à ce que l'application soit fermée et que les pointeurs statiques soient nettoyés.

#pragma once

#include <memory>

template<typename T>
class Singleton
{
private:
  static std::weak_ptr<T> _singleton;
public:
  static std::shared_ptr<T> singleton()
  {
    std::shared_ptr<T> singleton = _singleton.lock();
    if (!singleton) 
    {
      singleton.reset(new T());
      _singleton = singleton;
    }

    return singleton;
  }
};

template<typename T>
std::weak_ptr<T> Singleton<T>::_singleton;

0

Votre code est correct, sauf que vous n'avez pas déclaré le pointeur d'instance en dehors de la classe . Les déclarations de classe interne de variables statiques ne sont pas considérées comme des déclarations en C ++, mais cela est autorisé dans d'autres langages comme C # ou Java, etc.

class Singleton
{
   public:
       static Singleton* getInstance( );
   private:
       Singleton( );
       static Singleton* instance;
};
Singleton* Singleton::instance; //we need to declare outside because static variables are global

Vous devez savoir que l'instance Singleton n'a pas besoin d'être supprimée manuellement par nous . Nous en avons besoin d'un seul objet tout au long du programme, donc à la fin de l'exécution du programme, il sera automatiquement désalloué.


-1

Le document qui a été lié à ci-dessus décrit l'inconvénient du verrouillage à double vérification est que le compilateur peut allouer la mémoire pour l'objet et définir un pointeur sur l'adresse de la mémoire allouée, avant l'appel du constructeur de l'objet. Cependant, il est assez facile en c ++ d'utiliser des allocateurs pour allouer manuellement la mémoire, puis d'utiliser un appel de construction pour initialiser la mémoire. En utilisant cette approche, le verrouillage à double vérification fonctionne très bien.


2
Malheureusement non. Cela a été discuté en profondeur par certains des meilleurs développeurs C ++ du marché. Le verrouillage à double vérification est rompu en C ++ 03.
Martin York

-1
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

Exemple:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);

-1

Classe simple singleton, ce doit être votre fichier de classe d'en-tête

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

Accédez à votre singleton comme ceci:

sSingletonClass->Relocate(1, 2, 5);

-3

Je pense que vous devriez écrire une fonction statique dans laquelle votre objet statique est supprimé. Vous devez appeler cette fonction lorsque vous êtes sur le point de fermer votre application. Cela garantira que vous n'avez pas de fuite de mémoire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.