En un patrón singleton, por definición, la única instancia del objeto que puede existir se crea dentro de un método estático del propio objeto:
Código C++:
Ver originalclass Singleton
{
public:
static Singleton& Instance( )
{
static Singleton instance;
return instance;
}
}
Nota: Si te ha resultado extraño el valor de retorno del singleton te comento: Devolver un puntero es peligroso porque a alguien le puede entrar la tentación de ponerle un delete, mientras que si el método devuelve una referencia su uso se hace más natural... a nadie se le ocurre hacer un delete a una referencia.
Debido a este motivo y a que no se desea que haya más instancias pululando por la aplicación (por eso se aplica el patrón singleton), los constructores deben ser TODOS privados. El hecho de que sean privados no afecta al método
Instance porque, como hemos visto, pertenece a la propia clase, luego tiene acceso sin restricciones a los elementos de la clase.
Por otro lado tenemos que el compilador tiende a crear una versión por defecto del constructor por defecto, constructor copia y operador de asignación. Y digo tiende porque ésto solo sucede bajo ciertas condiciones:
- No se deben poner las firmas de las funciones en el archivo de cabecera. Si pones explicitamente la declaración del operador o del constructor en la cabecera el compilador se desentiende y es tu responsabilidad implementar la función
- En el caso de herencia, si la clase base tiene el constructor correspondiente como privado, el compilador no podrá crear la versión del constructor correspondiente en la clase hija.
Y por si con esto no había suficiente, el estándar C++11 añade más causística:
- Aparece un nuevo tipo de constructor Clase(Clase&&). Si no se declara explícitamente este constructor, el compilador puede intentar crear el constructor copia. Si se declara este constructor, el constructor copia no se implementará.
- Aparecen dos modificadores nuevos delete y default. El primero especifica que la función correspondiente no existe para la clase en cuestión, en este caso el compilador no creará la versión por defecto. El segundo obliga al compilador a crear la versión por defecto del constructor, aunque alguna de las reglas anteriores le indique lo contrario. Estos dos modificadores no pueden, obviamente, coexistir en la misma función.
Un par de ejemplos para ilustrarnos:
Código C++:
Ver original// Esta clase dispone de:
// * Constrtuctor por defecto
// * Constructor copia
// * Operador de asignación
class Test1
{
};
// Si no se implementa el constructor por defecto, se producirá
// un error al intentar llamarlo. Aunque no se pueda probar,
// esta clase cuenta con constructor copia y operador de asignación
class Test2
{
public:
Test2();
};
// Las 3 firmas son privadas, luego no podrán ser llamadas desde fuera
// de la clase. No obstante, si éstas funciones no se implementan, tampoco
// podrán ser llamadas desde la propia clase
class Test3
{
Test3();
Test3(const Test3&);
Test3& operator=(const Test3&);
};
// Adivina... no se pueden usar los constructores porque intentarán llamar al constructor correspondiente de Test3... que es privado.
class Test4 : public Test3
{
};
// Válido para C++11 en adelante
// El constructor por defecto queda anulado
// Se implementa el nuevo constructor de C++11
// Se obliga al compilador a crear el código por defecto para el constructor copia
// Si no se indica, el compilador no creará este constructor por existir el otro.
class Test5
{
public:
Test5() = delete;
Test5(Test5&&){ }
Test5(const Test5&) = default;
};
Dicho esto, qué forma ha de tener (o debería tener) un singleton? Bueno, salvo que necesites añadir más requisitos (como que la cadena de singleton se borren de una forma determinada al finalizar el programa para liberar recursos por ejemplo) debería tener esta forma:
Código C++:
Ver originalclass Singleton
{
public:
static Singleton& Instance()
{
static Singleton instance;
return instance;
}
private:
Singleton(); // No olvides implementar esta función
Singleton(const Singleton&); // No hace falta implementarla
Singleton(const Singleton&) = delete; // Opción 2 (C++11)
Singleton& operator=(const Singleton&); // No hace falta implementarla
Singleton& operator=(const Singleton&) = delete; // Opción 2 (C++11)
}
Por supuesto, la implementación puede estar fuera de la clase:
Código C++:
Ver originalclass Singleton
{
public:
static Singleton& Instance();
{
static Singleton instance;
return instance;
}
private:
Singleton(); // No olvides implementar esta función
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
}
// ...en otro fichero o donde quieras...
Singleton::Singleton()
{
// ...
}
Singleton& Singleton::Instance()
{
static Singleton instance;
return instance;
}
PD.:
Cita:
Iniciado por giuli956 Bueno estoy desarrollando el patron singleton en C++ utilizando Dev C++, y lo tengo ya listo, pero quiero aclarar conceptos.
yo tenia el constructor dentro de la clase como privado obviamente y no funcionaba, pero luego encontre en internet:
Ese ';' detrás de las llaves va a dar error de compilación. Y lo mismo te digo sobre el otro punto y coma que está justo después de salir de la función
getInstance
Un saludo.