Patrón de diseño C++ Singleton

11 minutos de lectura

Patron de diseno C Singleton
barquero artem

Recientemente me encontré con una realización/implementación del patrón de diseño Singleton para C++. Se ha visto así (lo he adoptado del ejemplo de la vida real):

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

De esta declaración, puedo deducir que el campo de instancia se inicia en el montón. Eso significa que hay una asignación de memoria. Lo que no está del todo claro para mí es cuándo se desasignará exactamente la memoria. ¿O hay un error y una fuga de memoria? Parece que hay un problema con la implementación.

Mi pregunta principal es, ¿cómo lo implemento de la manera correcta?

  • Consulte también: stackoverflow.com/questions/211237/… y stackoverflow.com/questions/270947/… y stackoverflow.com/questions/246564/… y stackoverflow.com/questions/449436/… y stackoverflow.com/questions/335369 /…

    – Martín York

    17 de junio de 2009 a las 16:43

  • Encontrará una excelente discusión sobre cómo implementar un singleton, junto con la seguridad de subprocesos en C++ en este documento. aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf

    Matthieu N.

    30 de octubre de 2009 a las 11:34

  • @sbi: solo un Sith trata con absolutos. ¿Se puede resolver la gran mayoría de los problemas sin Singletons? Absolutamente. ¿Los Singletons causan sus propios problemas? Si. Sin embargo, no puedo decir honestamente que sean maloya que el diseño se trata de considerar las compensaciones y comprender los matices de su enfoque.

    – derekerdmann

    28 de julio de 2011 a las 20:10

  • @derekerdmann: No dije que nunca necesitas una variable global (y cuando la necesitas, un Singleton a veces es mejor). Lo que dije es que se deben usar lo menos posible. Glorificar a Singleton como un patrón de diseño valioso da la impresión de que es bueno usarlo, en lugar de que sea un cortar a tajos, lo que hace que el código sea difícil de entender, difícil de mantener y difícil de probar. Por eso publiqué mi comentario. Nada de lo que has dicho hasta ahora contradice esto.

    – sbi

    29 de julio de 2011 a las 13:26

  • @sbi: Lo que dijiste fue “No los uses”. No es mucho más razonable “deben usarse lo menos posible” a lo que luego cambió, seguramente ve la diferencia.

    – jwd

    17/10/2011 a las 22:21

Patron de diseno C Singleton
martín york

En 2008, proporcioné una implementación en C++98 del patrón de diseño Singleton que se evalúa de forma perezosa, tiene destrucción garantizada y no es técnicamente seguro para subprocesos:
¿Alguien puede proporcionarme una muestra de Singleton en c ++?

Aquí hay una implementación actualizada de C++ 11 del patrón de diseño Singleton que se evalúa de forma diferida, se destruye correctamente y es seguro para subprocesos.

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 inaccessible(especially from outside), 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
};

Consulte este artículo sobre cuándo usar un singleton: (no a menudo)
Singleton: ¿Cómo se debe usar?

Vea estos dos artículos sobre el orden de inicialización y cómo manejarlo:
Orden de inicialización de variables estáticas
Encontrar problemas de orden de inicialización estática de C++

Vea este artículo que describe vidas:
¿Cuál es la vida útil de una variable estática en una función de C++?

Consulte este artículo que analiza algunas implicaciones de subprocesamiento para singletons:
Instancia Singleton declarada como variable estática del método GetInstance, ¿es seguro para subprocesos?

Consulte este artículo que explica por qué el bloqueo de verificación doble no funcionará en C++:
¿Cuáles son todos los comportamientos indefinidos comunes que un programador de C++ debería conocer?
Dr. Dobbs: C++ y los peligros del bloqueo doblemente verificado: Parte I

  • Buena respuesta. Pero debe tener en cuenta que esto no es seguro para subprocesos stackoverflow.com/questions/1661529/…

    – Varuna

    25 de diciembre de 2009 a las 8:28

  • Ya se señaló anteriormente en: stackoverflow.com/questions/449436/…

    – Martín York

    25 de diciembre de 2009 a las 9:44

  • @zourtney: Mucha gente no se da cuenta de lo que acabas de hacer 🙂

    -Johann Gerell

    4 de enero de 2013 a las 7:06

  • @MaximYegorushkin: Cuando esto se destruye es muy bien definido (no hay ambigüedad). Ver: stackoverflow.com/questions/246564/…

    – Martín York

    20 mayo 2013 a las 20:39

  • What irks me most though is the run-time check of the hidden boolean in getInstance() Esa es una suposición sobre la técnica de implementación. No es necesario suponer que está vivo. consulte stackoverflow.com/a/335746/14065 Puede forzar una situación para que siempre esté viva (menos gastos generales que Schwarz counter). Las variables globales tienen más problemas con el orden de inicialización (entre unidades de compilación) ya que no se fuerza un orden. La ventaja de este modelo es 1) inicialización diferida. 2) Habilidad para hacer cumplir una orden (Schwarz ayuda pero es más feo). sí get_instance() es mucho mas feo

    – Martín York

    20 mayo 2013 a las 23:07

Al ser un Singleton, normalmente no quieres que se destruya.

Será derribado y desasignado cuando finalice el programa, que es el comportamiento normal y deseado para un singleton. Si desea poder limpiarlo explícitamente, es bastante fácil agregar un método estático a la clase que le permita restaurarlo a un estado limpio y reasignarlo la próxima vez que se use, pero eso está fuera del alcance de un singleton “clásico”.

  • No es una fuga de memoria más que una simple declaración de una variable global.

    – ilya n.

    17 de junio de 2009 a las 16:14

  • Para aclarar algo… las preocupaciones de “fuga de memoria” con respecto a los singletons son completamente irrelevantes. Si tiene recursos con estado en los que importa el orden de deconstrucción, los singletons pueden ser peligrosos; pero toda la memoria es recuperada limpiamente por el sistema operativo al finalizar el programa… anulando este punto totalmente académico en el 99,9% de los casos. Si desea discutir la gramática de lo que es y no es una “fuga de memoria”, está bien, pero tenga en cuenta que es una distracción de las decisiones de diseño reales.

    – jkerian

    17 de junio de 2009 a las 16:21

  • @jkerian: las fugas de memoria y la destrucción en el contexto de C ++ no se trata realmente de la fuga de memoria. Realmente se trata de control de recursos. Si pierde memoria, no se llama al destructor y, por lo tanto, los recursos asociados con el objeto no se liberan correctamente. La memoria es solo el ejemplo simple que usamos cuando enseñamos programación, pero existen recursos mucho más complejos.

    – Martín York

    17 de junio de 2009 a las 16:34

  • @Martin Estoy completamente de acuerdo contigo. Incluso si el único recurso es la memoria, aún tendrá problemas para encontrar fugas REALES en su programa si tiene que revisar una lista de fugas, filtrando las que “no importan”. Es mejor limpiar todo esto para que cualquier herramienta que informe fugas solo informe cosas que SON un problema.

    – Delfín

    17 de junio de 2009 a las 16:53

  • Vale la pena considerar vagamente que existen implementaciones de C ++ (potencialmente incluso alojadas) en las que el “SO” no recupere todos los recursos cuando su programa salga, pero que tengan algún concepto de “ejecutar su programa nuevamente”, lo que le brinda un nuevo conjunto de globales y locales estáticos. En tales sistemas, un singleton no liberado es una fuga genuina según cualquier definición sensata: si su programa se ejecuta suficientes veces, desactivará el sistema. Si le importa la portabilidad a tales sistemas es otra cuestión, siempre que no esté escribiendo una biblioteca, es casi seguro que no.

    –Steve Jessop

    16 de enero de 2014 a las 10:04


Podría evitar la asignación de memoria. Hay muchas variantes, todas con problemas en el caso de un entorno de subprocesos múltiples.

Prefiero este tipo de implementación (en realidad, no se dice correctamente que prefiero, porque evito los singletons tanto como sea posible):

class Singleton
{
private:
   Singleton();

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

No tiene asignación de memoria dinámica.

  • En algunos casos, esta inicialización perezosa no es el patrón ideal a seguir. Un ejemplo es si el constructor del singleton asigna memoria del montón y desea que esa asignación sea predecible, por ejemplo, en un sistema integrado u otro entorno estrictamente controlado. Prefiero, cuando el patrón Singleton es el mejor patrón para usar, crear la instancia como un miembro estático de la clase.

    – dma

    17 de junio de 2009 a las 17:06

  • Para muchos programas más grandes, especialmente aquellos con bibliotecas dinámicas. Cualquier objeto global o estático que no sea primitivo puede provocar fallas de segmento/bloqueos al salir del programa en muchas plataformas debido a problemas de orden de destrucción al descargar las bibliotecas. Esta es una de las razones por las que muchas convenciones de codificación (incluida la de Google) prohíben el uso de objetos globales y estáticos no triviales.

    – obecalp

    17 de junio de 2009 a las 18:04

  • Parece que la instancia estática en dicha implementación tiene un enlace interno y tendrá copias únicas e independientes en diferentes unidades de traducción, lo que provocará un comportamiento incorrecto y confuso. Pero vi muchas implementaciones de este tipo, ¿me estoy perdiendo algo?

    – FaceBro

    25 de febrero de 2017 a las 14:12

  • ¿Qué impide que el usuario asigne esto a múltiples objetos donde el compilador detrás de escena usa su propio constructor de copia?

    – Tony Tannous

    15 de julio de 2019 a las 20:46

  • @Tony Nada impide copiar, tienes razón. El constructor de copia debe eliminarse.

    – ebrahim

    23 mayo 2021 a las 17:24


Patron de diseno C Singleton
Galik

La respuesta de @Loki Astari es excelente.

Sin embargo, hay ocasiones con múltiples objetos estáticos en los que necesita poder garantizar que el único no serán destruidos hasta que todos sus objetos estáticos que usan el único ya no lo necesito.

En este caso std::shared_ptr puede usarse para mantener el único vivo para todos los usuarios, incluso cuando se llama a los destructores estáticos al final del programa:

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() {}
};

Otra alternativa sin asignación: crear un singleton, digamos de clase Ccomo lo necesites:

singleton<C>()

utilizando

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

Ni esta respuesta ni la de Cătălin son automáticamente seguras para subprocesos en C++ actual, pero estarán en C++0x.

  • Actualmente bajo gcc es seguro para subprocesos (y lo ha sido por un tiempo).

    – Martín York

    17 de junio de 2009 a las 16:26

  • El problema con este diseño es que si se usa en varias bibliotecas. Cada biblioteca tiene su propia copia del singleton que usa esa biblioteca. Así que ya no es un singleton.

    – Martín York

    17 de junio de 2009 a las 16:27


No encontré una implementación CRTP entre las respuestas, así que aquí está:

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

Para usar simplemente herede su clase de esto, como: class Test : public Singleton<Test>

  • Actualmente bajo gcc es seguro para subprocesos (y lo ha sido por un tiempo).

    – Martín York

    17 de junio de 2009 a las 16:26

  • El problema con este diseño es que si se usa en varias bibliotecas. Cada biblioteca tiene su propia copia del singleton que usa esa biblioteca. Así que ya no es un singleton.

    – Martín York

    17 de junio de 2009 a las 16:27


1647661095 33 Patron de diseno C Singleton
último golpe

Aquí hay una implementación fácil.

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

Solo se crea un objeto y esta referencia de objeto se devuelve todas y cada una de las palabras posteriores.

SingletonClass instance created!
00915CB8
00915CB8

Aquí 00915CB8 es la ubicación de memoria del objeto singleton, igual durante la duración del programa pero (¡normalmente!) diferente cada vez que se ejecuta el programa.

NB Esto no es seguro para subprocesos. Debe garantizar la seguridad de los subprocesos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad