Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Entendiendo el patrón Singleton.

Estas en el tema de Entendiendo el patrón Singleton. en el foro de C/C++ en Foros del Web. Hola: Ahora que tengo algo de tiempo, he retomado el C++ y en este caso iba a ver si profundizo en el tema de patrones. ...
  #1 (permalink)  
Antiguo 20/11/2015, 06:13
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 7 meses
Puntos: 10
Entendiendo el patrón Singleton.

Hola:

Ahora que tengo algo de tiempo, he retomado el C++ y en este caso iba a ver si profundizo en el tema de patrones. Para ello he empezado con el que se supone que es el más sencillo, el Singleton.

La implementación está casi fusilada de uno de los muchos ejemplos que hay en la red, pero cuando he querido hacer mis enredos han sido todo problemas.

Esta es la implementación:

Singleton.h
Código C++:
Ver original
  1. #ifndef SINGLETON_H
  2. #define SINGLETON_H
  3.  
  4.  
  5. class Singleton
  6. {
  7.     public:
  8.         static Singleton* getInstance();
  9.         virtual ~Singleton();
  10.         int VerValor();
  11.         void duplicar();
  12.  
  13.     protected:
  14.         Singleton();
  15.  
  16.     private:
  17.         static Singleton* instancia;
  18.         int valor;
  19.  
  20. };
  21.  
  22. #endif // SINGLETON_H

Singleton cpp
Código C++:
Ver original
  1. #include "Singleton.h"
  2. #include <iostream>
  3.  
  4. Singleton*::Singleton::instancia=nullptr;
  5.  
  6. Singleton::Singleton():valor(5){}
  7.  
  8. Singleton::~Singleton()
  9. {
  10.     std::cout<<"Borrando....";
  11.     delete instancia;
  12. }
  13.  
  14. Singleton*::Singleton::getInstance()
  15. {
  16.      if(instancia == nullptr)
  17.      {
  18.          instancia = new Singleton();
  19.          //valor=v;
  20.      }
  21.   return instancia;
  22. }
  23.  
  24. int Singleton::VerValor()
  25. {
  26.     return valor;
  27. }
  28.  
  29. void Singleton::duplicar()
  30. {
  31.     valor*=2;
  32. }

main.cpp
Código C++:
Ver original
  1. #include <iostream>
  2. #include "Singleton.h"
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8.     Singleton* S;
  9.     S->getInstance();
  10.     cout<<S->VerValor();
  11.     delete S;
  12.     return 0;
  13.  
  14. }

Y las dudas son:

Si llamo a la función VerValor() obtengo error de segmentación, pero no entiendo el por qué.
Se supone que en la creación de la instancia, le doy un valor al miembro "valor". No sé por qué me da error al retornarlo,
Y cuando borro el puntero, no se me muestra el mensaje del destructor. ¿Y eso por que?

Y otra cosa:

Si quiero crear la instancia de la clase con algún argumento (no sé si se dará el caso en alguna aplicación práctica, pero bueno, por probar) lo intento así:

Código C++:
Ver original
  1. Singleton*::Singleton::getInstance(int v)
  2. {
  3.      if(instancia == nullptr)
  4.      {
  5.          instancia = new Singleton();
  6.          valor=v;
  7.      }
  8.   return instancia;
  9. }

Pero me da el error:
include/Singleton.h|18|error: invalid use of member ‘Singleton::valor’ in static member function|

Gracias como siempre
__________________
Mi calculadora en Qt
  #2 (permalink)  
Antiguo 21/11/2015, 05:55
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Entendiendo el patrón Singleton.

Te contestó desde el móvil, así que disculpas si no queda del todo organizado.

Para empezar un singleton NO DEBE BORRARSE, ya que el puntero "instance" no se entera y se queda apuntando a una dirección no válida. Para evitar ese tipo de tentaciones lo mejor es devolver una referencia, ya que a nadie se le ocurre convertir una referencia en puntero para borrarla.

En segundo lugar, queda más limpio si evitas usar una variable estática a nivel de clase, es mejor declararla estática a nivel de función:

Código C++:
Ver original
  1. clases Singleton
  2. {
  3. public:
  4.   static Singleton& Instance()
  5.   {
  6.     static Singleton instance;
  7.     return instance;
  8.   }
  9. };

Además, lo suyo es declarar los constructores privados, para evitar que se hagan copias no autorizadas del objeto. No sólo el constructor por defecto, sino también el constructor copia y, por costumbre, el operador de asignación.

La excepción te salta porque getInstance es un método estático... Es decir, hay que llamarlo S=Singleton::getInstance(). Además que S es un puntero no inicializado... cualquier uso de ese puntero te dará error.

Y para terminar, Singleton se usa cuando se necesita una única instancia del objeto y que este sea global a toda la aplicación. No veo por qué querrías crear un objeto con argumentos... Y si después pides otro con argumentos diferentes?? En esos casos no necesitas un Singleton sino objetos comunes.

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #3 (permalink)  
Antiguo 23/11/2015, 10:05
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 7 meses
Puntos: 10
Respuesta: Entendiendo el patrón Singleton.

Cita:
Iniciado por eferion Ver Mensaje
Te contestó desde el móvil, así que disculpas si no queda del todo organizado.

Para empezar un singleton NO DEBE BORRARSE, ya que el puntero "instance" no se entera y se queda apuntando a una dirección no válida. Para evitar ese tipo de tentaciones lo mejor es devolver una referencia, ya que a nadie se le ocurre convertir una referencia en puntero para borrarla.

En segundo lugar, queda más limpio si evitas usar una variable estática a nivel de clase, es mejor declararla estática a nivel de función:

Código C++:
Ver original
  1. clases Singleton
  2. {
  3. public:
  4.   static Singleton& Instance()
  5.   {
  6.     static Singleton instance;
  7.     return instance;
  8.   }
  9. };

Además, lo suyo es declarar los constructores privados, para evitar que se hagan copias no autorizadas del objeto. No sólo el constructor por defecto, sino también el constructor copia y, por costumbre, el operador de asignación.

La excepción te salta porque getInstance es un método estático... Es decir, hay que llamarlo S=Singleton::getInstance(). Además que S es un puntero no inicializado... cualquier uso de ese puntero te dará error.

Y para terminar, Singleton se usa cuando se necesita una única instancia del objeto y que este sea global a toda la aplicación. No veo por qué querrías crear un objeto con argumentos... Y si después pides otro con argumentos diferentes?? En esos casos no necesitas un Singleton sino objetos comunes.

Un saludo
Gracias como siempre, eferion. Ya tengo base para seguir investigando.
__________________
Mi calculadora en Qt
  #4 (permalink)  
Antiguo 25/11/2015, 04:07
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 7 meses
Puntos: 10
Respuesta: Entendiendo el patrón Singleton.

Bueno, pues aquí va una implementación mejor (casi nada es mío):
Código C++:
Ver original
  1. #ifndef SINGLETON_H
  2. #define SINGLETON_H
  3.  
  4.  
  5. class Singleton
  6. {
  7.     public:
  8.         Singleton (const Singleton&) = delete;
  9.         Singleton& operator=(const Singleton&) = delete;
  10.         static Singleton& getInstance();
  11.         int VerValor();
  12.         void duplicar();
  13.         ~Singleton();
  14.  
  15.     private:
  16.         Singleton();
  17.         static Singleton* instancia;
  18.         int valor;
  19.  
  20. };
  21.  
  22. #endif // SINGLETON_H

Código C++:
Ver original
  1. #include "Singleton.h"
  2. #include <iostream>
  3.  
  4. Singleton::Singleton():valor(5){}
  5.  
  6. Singleton::~Singleton()
  7. {
  8.     std::cout<<"Borrando....";
  9. }
  10.  
  11. Singleton& Singleton::getInstance()
  12. {
  13.      static Singleton instancia;
  14.      return instancia;
  15. }
  16.  
  17. int Singleton::VerValor()
  18. {
  19.     return valor;
  20. }
  21.  
  22. void Singleton::duplicar()
  23. {
  24.     valor*=2;
  25. }

Código C++:
Ver original
  1. #include <iostream>
  2. #include "Singleton.h"
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8.     Singleton& S = Singleton::getInstance();
  9.     Singleton& S2 = Singleton::getInstance();
  10.     S.duplicar();
  11.     cout<<S2.VerValor()<<endl;
  12.     return 0;
  13.  
  14. }

Por comentar las observaciones que he hecho....
Al principio no había hecho el uso de "delete" en los constructores de copia y asignación y he creado la instancia al modo "clásico"

Código C++:
Ver original
  1. Singleton S3(Singleton::getInstance());

Pero al ver el destructor me sonaba raro que se invocase 2 veces al mismo.
Luego he visto que lo suyo es prohibir estos constructores. A cambio, me obliga a crear la instancia como referencias.

Ahora, por la forma de ser creada la instancia, entiendo que no tiene sentido que se le pongan parámetros.

Lo que no entiendo muy bien es el por qué de este patrón. Es decir, en cierto modo estoy "enmascarando" que me remito siempre al mismo objeto, pero ese comportamiento lo puedo hacer igual con punteros, entiendo yo.
__________________
Mi calculadora en Qt
  #5 (permalink)  
Antiguo 25/11/2015, 04:31
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Entendiendo el patrón Singleton.

Cita:
Iniciado por dehm Ver Mensaje
Por comentar las observaciones que he hecho....
Al principio no había hecho el uso de "delete" en los constructores de copia y asignación y he creado la instancia al modo "clásico"

Código C++:
Ver original
  1. Singleton S3(Singleton::getInstance());

Pero al ver el destructor me sonaba raro que se invocase 2 veces al mismo.
Luego he visto que lo suyo es prohibir estos constructores. A cambio, me obliga a crear la instancia como referencias.
Esa línea, como comentas está mal. Como bien as notado, el patrón singleton pretende que únicamente haya una instancia del objeto para toda la aplicación y ahí estás intentando obtener una copia del objeto... lo cual lleva inevitablemente a que exista mas de un objeto.

Puede sonar un poco raro usar una variable como referencia, pero se pueden optimizar bastantes partes del código con esa tontería de las referencias. Hay que recordar que usar una referencia implica que no se llama al constructor copia del objeto...

La forma correcta de llamar a getInstance sería la siguiente:

Código C++:
Ver original
  1. Singleton& S3 = Singleton::getInstance();

Cita:
Iniciado por dehm Ver Mensaje
Ahora, por la forma de ser creada la instancia, entiendo que no tiene sentido que se le pongan parámetros.
Esa es una buena noticia, quiere decir que te van quedando claros los detalles del patrón :)

Cita:
Iniciado por dehm Ver Mensaje
Lo que no entiendo muy bien es el por qué de este patrón. Es decir, en cierto modo estoy "enmascarando" que me remito siempre al mismo objeto, pero ese comportamiento lo puedo hacer igual con punteros, entiendo yo.
La principal ventaja de este patrón con respecto al uso de punteros es que no tienes que arrastrar los punteros por todo tu código. Imagínate que en un proyecto tienes que arrastrar 5 punteros por todas tus funciones porque no te dejan usar este patrón... tus funciones tendrían los parámetros necesarios para funcionar y un plus de 5 parámetros para arrastrar los punteros.

Como contrapartida, con este patrón se pierde un poco el hilo de la ejecución. El acceso al Singleton es libre y queda en manos del desarrollador el hacer un uso responsable del mismo... si en vez del singleton arrastras el puntero es muy sencillo prohibir el acceso al mismo simplemente no proporcionándolo.

A mi personalmente no me gusta demasiado tener que depender de este patrón, si hay razones de peso suficientes vale, pero con cautela. Es muy facil caer en la tentación y convertir toda la aplicación en una sucesión de singleton...

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #6 (permalink)  
Antiguo 25/11/2015, 04:59
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 7 meses
Puntos: 10
Respuesta: Entendiendo el patrón Singleton.

Muchas gracias eferion. ¿Qué haríamos sin ti en el foro?

Bueno, marco como solucionado y cuando haga alguna prueba más con este me pondré con otro patrón.

¡Saludos!
__________________
Mi calculadora en Qt

Etiquetas: int
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 07:53.