web-dev-qa-db-fra.com

Quelqu'un peut-il me fournir un échantillon de Singleton en c ++?

J'écris un singleton c ++ de la manière suivante:

class A {
    private:
        static A* m_pA;
        A();
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL)
        m_pA = new A();
    return m_pA;
}

A::~A() {
    FreeInstance()  // Can I write this? are there any potential error?
}

void A::FreeInstance() {
    delete m_pA;
    m_pA = NULL;
}

Merci! Evan Teran et la réponse de sep61.myopenid.com sont exactes et vraiment bonnes! Mon chemin est mauvais, je souhaite que toute personne écrivant un tel code puisse éviter mon erreur idiote.

Mon singleton A dans mon projet a un vecteur de pointeur intelligent et un autre thread peut également éditer ce vecteur. Ainsi, lorsque l'application se ferme, elle devient toujours instable même si j'ajoute beaucoup de CMutex. Erreur multithread + erreur singleton m'a gaspillé 1 jour.

// ------------------------------------------------- -------------- Un nouveau singleton, que vous pouvez modifier si vous pensez que l’exemple suivant présente un problème:

class A {
    private:
        static A* m_pA;
        explicit A();
        void A(const A& a);
        void A(A &a);
        const A& operator=(const A& a);
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL){
        static A self;
        m_pA = &self;
    }
    return m_pA;
}

A::~A() {
}
35
user25749

Vous pouvez éviter de devoir le supprimer en utilisant un objet statique comme celui-ci:

if(m_pA == 0) {
    static A static_instance;
    m_pA = &static_instance;
}
12
Evan Teran

Pourquoi tout le monde veut-il renvoyer un singleton en tant que pointeur?
Le retourner car une référence semble beaucoup plus logique!

Vous ne devriez jamais pouvoir libérer un singleton manuellement. Comment savez-vous qui garde une référence au singleton? Si vous ne savez pas (ou ne pouvez pas garantir) que personne n'a de référence (dans votre cas, via un pointeur), vous n'avez alors pas à libérer l'objet.

Utilisez le statique dans une méthode de fonction.
Cela garantit qu'il est créé et détruit une seule fois. Il vous donne également l'initialisation paresseuse gratuitement. 

class S
{
    public:
        static S& getInstance()
        {
            static S    instance;
            return instance;
        }
    private:
        S() {}
        S(S const&);              // Don't Implement.
        void operator=(S const&); // Don't implement
 };

Notez que vous devez également rendre le constructeur privé .. .. Assurez-vous également de remplacer le constructeur de copie par défaut et l'opérateur d'affectation afin que vous ne puissiez pas copier le singleton (sinon ce ne serait pas un singleton).

Lisez aussi:

Pour vous assurer que vous utilisez un singleton pour les bonnes raisons.

Bien que techniquement non thread-safe dans le cas général, voir:
Quelle est la durée de vie d'une variable statique dans une fonction C++?

GCC a un patch explicite pour compenser cela:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html

191
Martin York

Un singleton en C++ peut être écrit de cette façon:

static A* A::GetInstance() {
    static A sin;
    return &sin;
}
4
sep

N'oubliez pas de rendre le constructeur de copie et les opérateurs d'affectation privés.

2
Jasper Bekkers

Je ne pense pas qu'il y ait de raison d'écrire cette ligne non. Votre méthode de destructeur n'est pas statique et votre instance singleton ne sera pas détruite de cette manière. Je ne pense pas que le destructeur soit nécessaire, si vous devez nettoyer l'objet, utilisez la méthode statique que vous avez déjà créée, FreeInstance ().

En dehors de cela, vous créez vos singletons à peu près de la même façon que je crée les miens.

1
Odd

Après une période d'enthousiasme déchaîné pour les singletons de style Meyers (en utilisant des objets statiques locaux comme dans certaines des réponses précédentes), je suis tombé complètement malade des problèmes de gestion de la durée de vie dans les applications compliquées.

J'ai tendance à constater que vous finissez par référencer la méthode 'Instance' délibérément au début de l'initialisation de l'application, pour vous assurer qu'elle est créée quand vous le souhaitez, puis que vous jouez à toutes sortes de jeux avec la destruction en raison de l'imprévisible (ou au moins très compliqué et quelque peu caché) dans lequel les choses sont détruites.

YMMV bien sûr, et cela dépend un peu de la nature du singleton lui-même, mais une grande partie de la gaufre sur les singletons intelligents (et les problèmes de filetage/verrouillage qui entourent l’habileté) est surestimée à l’OMI.

1
Will Dean

si vous lisez "Design moderne en C++", vous réaliserez qu'un design singleton peut être beaucoup plus complexe que de renvoyer une variable statique. 

1
jab

Cette implémentation est correcte tant que vous pouvez répondre à ces questions:

  1. savez-vous quand l'objet sera créé (si vous utilisez un objet statique au lieu d'un nouvel? Avez-vous un main ()?)

  2. votre singleton a-t-il des dépendances qui ne sont peut-être pas prêtes au moment de sa création? Si vous utilisez un objet statique au lieu d'un nouvel objet, quelles bibliothèques ont été initialisées à ce moment? Qu'est-ce que votre objet fait dans le constructeur qui pourrait en avoir besoin?

  3. quand sera-t-il supprimé?

Utiliser new () est plus sûr car vous contrôlez où et quand l'objet sera créé et supprimé. Mais ensuite, vous devez le supprimer explicitement et probablement personne dans le système ne sait quand le faire. Vous pouvez utiliser atexit () pour cela, si cela a du sens.

Utiliser un objet statique dans la méthode signifie que vous ne savez pas vraiment quand il sera créé ou supprimé. Vous pouvez également utiliser un objet statique global dans un espace de noms et éviter getInstance () du tout - cela n'ajoute pas grand chose.

Si vous utilisez des threads, vous avez de gros problèmes. Il est pratiquement impossible de créer un singleton thread-safe utilisable en C++ en raison de:

  1. le verrouillage permanent dans getInstance est très lourd - un changement de contexte complet à chaque getInstance ()
  2. le verrouillage double vérifié échoue en raison des optimisations du compilateur et du modèle de mémoire cache/faible, est très délicat à mettre en œuvre et impossible à tester. Je n'essaierais pas de le faire dans un système réel, à moins de connaître intimement votre architecture et de vouloir qu'elle ne soit pas portable.

Ceux-ci peuvent être facilement recherchés sur Google, mais voici un bon lien sur le modèle de mémoire faible: http://ridiculousfish.com/blog/archives/2007/02/17/barrier .

Une solution consisterait à utiliser le verrouillage, mais obligerait les utilisateurs à mettre en cache le pointeur obtenu de getInctance () et à se préparer à ce que getInstance () soit lourd.

Une autre solution serait de laisser les utilisateurs gérer eux-mêmes la sécurité des threads.

Une autre solution consisterait à utiliser une fonction avec verrouillage simple et à la remplacer par une autre fonction sans verrouillage ni vérification une fois l'appel de la nouvelle (). Cela fonctionne, mais sa mise en œuvre complète est compliquée.

0
n-alexander
//! @file singleton.h
//!
//! @brief Variadic template to make a singleton out of an ordinary type.
//!
//! This template makes a singleton out of a type without a default
//! constructor.

#ifndef SINGLETON_H
#define SINGLETON_H

#include <stdexcept>

template <typename C, typename ...Args>
class singleton
{
private:
  singleton() = default;
  static C* m_instance;

public:
  singleton(const singleton&) = delete;
  singleton& operator=(const singleton&) = delete;
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;

  ~singleton()
  {
    delete m_instance;
    m_instance = nullptr;
  }

  static C& create(Args...args)
  {
    if (m_instance != nullptr)
      {
    delete m_instance;
    m_instance = nullptr;
      }
    m_instance = new C(args...);
    return *m_instance;
  }

  static C& instance()
  {
    if (m_instance == nullptr)
      throw std::logic_error(
        "singleton<>::create(...) must precede singleton<>::instance()");
    return *m_instance;
  }
};

template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;

#endif // SINGLETON_H
0
amightywind

Il existe une excellente bibliothèque C++, ACE, basée sur des modèles. Il y a beaucoup de documentation sur différents types de motifs, regardez leur travail: http://www.cs.wustl.edu/~schmidt/ACE.html

0