¿Cómo se implementa el patrón de diseño Singleton?
Recientemente me encontré con una realización/implementación del patrón de diseño Singleton para C++. Se veía 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 me queda del todo claro es cuándo exactamente se desasignará la memoria. ¿O hay un error y una pérdida de memoria? Parece que hay un problema con la implementación.
Mi pregunta principal es, ¿cómo lo implemento de la manera correcta?
En 2008, proporcioné una implementación en C++98 del patrón de diseño Singleton que es evaluado de forma diferida, 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 C++11 actualizada del patrón de diseño Singleton que se evalúa de forma diferida, se destruye correctamente y es segura 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 con frecuencia)
Singleton: ¿Cómo se debe usar?
Consulte estos dos artículos sobre el orden de inicialización y cómo solucionarlo:
Orden de inicialización de variables estáticas
Encontrar problemas de orden de inicialización estática de C++
Consulte este artículo que describe la vida útil:
¿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 los subprocesos para los singleton:
Instancia singleton declarada como variable estática del método GetInstance, ¿es segura para subprocesos?
Consulte este artículo que explica por qué el bloqueo de doble verificación no funciona 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 de doble verificación: Parte I
Podrías evitar la asignación de memoria. Hay muchas variantes y todas tienen 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 singleton 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.
Al ser Singleton, normalmente no querrás que lo destruyan.
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".