Singleton-Eigenschaft erben
-
Hallo Leute,
ich habe ein Problem, über das wahrscheinlich jeder irgendwann stolpert... Meine googelei hat ergeben, dass es anscheinend keine (zufriedenstellende) Lösung für dieses Problem gibt. Nun möchte ich hier eine Diskussion über "das kleinste Übel" der möglichen Workarounds anregen:
template< typename T > class base_t { private: static T *m_singleton; private: base_t( ) { }; base_t( const base_t & ) { }; base_t& operator=( const base_t & ) { }; public: static T& get_instance( ); virtual ~base_t( ) = 0 { }; }; template< typename T > T *base_t< T >::m_singleton = 0; template< typename T > T& base_t< T >::get_instance( ) { if( !m_singleton ) { m_singleton = new T( ); } return *m_singleton; } class derived_t : public base_t< derived_t > { public: void say_hello( ); };Soweit, so schlecht. Funktioniert natürlich nicht, da der c-tor der Basisklasse nicht für die abgeleitete Klasse zugreifbar ist.
a) in
basis_t:frient T;
b) Standard-c-tor der Basisklasseprotected;
c) ???Also, wie denn nun?
PS: Wird es denn in C++0x eine Lösung dafür geben?
-
Was spricht denn gegen die 2. Variante? Wenn du von base_t erben willst, dann sollte doch der Konstruktor mindestens 'protected' sein.
Du scheinst auch einen nicht ganz standard-konformen Compiler zu benutzen (MSVC?). Statt "virtual ~base_t( ) = 0 { };" müßtest du eigentlich die Definition noch mal separat hinschreiben.
Auch ein "friend T" ist nicht standardkonform (wird aber auch vom MSVC unterstützt), soll aber bei C++0x erlaubt werden (siehe query_boy's Artikel: http://www.c-plusplus.net/forum/viewtopic-var-t-is-204654.html).Außerdem brauchst (bzw. solltest) du den Destruktor nicht virtuell zu deklarieren. Du wirst ja schließlich keine Polymorphie bei einem Singleton einsetzen wollen -)
-
~Single schrieb:
Was spricht denn gegen die 2. Variante?
... dass wieder alles Essig ist, mit meinem Singleton. Da kann ich mir den Aufwand gleich sparen.
~Single schrieb:
Du scheinst auch einen nicht ganz standard-konformen Compiler zu benutzen (MSVC?). Statt "virtual ~base_t( ) = 0 { };" müßtest du eigentlich die Definition noch mal separat hinschreiben.
Geändert.
~Single schrieb:
Auch ein "friend T" ist nicht standardkonform (wird aber auch vom MSVC unterstützt), soll aber bei C++0x erlaubt werden.
Dann soll's mir recht sein.
Lösung hab' ich immer noch keine. Gibt es sie?
greetz, Swordfish
-
Ich halte eine Singletonbasisklasse nicht so günstig:
- der Singletonmechanismus hängt an einem Punkt von der eigentlichen Klasse ab: dort, wo das Objekt erzeugt bzw. zerstört wird
- umgekehrt gibt es keine Abhängigkeit der eigentlichen Klasse von der Singletonbasis, lediglich von den durch sie durchgesetzten GarantienIMO ist es daher sinnvoller, die Vererbung anders herum ablaufen zu lassen:
template< typename T > class singleton : public T { private: static singleton* m_instance; private: singleton() {} singleton(singleton&); void operator=(singleton&); public: static singleton& get_instance(); }; template< typename T > singleton< T >* singleton< T >::m_instance = 0; template< typename T > singleton< T >& singleton< T >::get_instance() { if( !m_instance ) { m_instance = new singleton< T >; } return *m_instance; } class some_class_impl_t { friend class singleton< some_class_impl_t >; private: ~some_class_impl_t() {} public: void say_hello(); }; typedef singleton< some_class_impl_t > some_class_t;Letzten Monat hat ein Review einer Singletonbibliothek bei boost stattgefunden. Ich hab mir das nicht angeschaut, aber vielleicht gibt es dort ein paar interessante Antworten.
-
Ich hab' bei meinen Recherchen die Singleton Bibliothek gefunden, auch das Review Announcement, konnte aber die einzelnen Reviews nicht finden?
Deine Lösung finde ich schon ganz passabel!

Hab' noch den Rückgabetyp von
singleton::get_instance( )geändert:template< typename T > class singleton : public T { private: static T* m_instance; singleton( ) { } singleton( singleton& ); singleton< T >& operator=( const singleton & ); // ^^ Sag' mal, warum kann ich mir den Rückgabetyp sparen? public: static T& get_instance( ); }; template< typename T > T* singleton< T >::m_instance = 0; template< typename T > T& singleton< T >::get_instance() { if( !m_instance ) { m_instance = new T; } return *m_instance; } class some_class_impl_t { friend class singleton< some_class_impl_t >; private: ~some_class_impl_t( ) { } };Somit hängt die Singleton-Eigenschaft nur noch von einem privatem dtor des "Clients" (<-ist die Bezeichnung möglich?) ab, wenn ich das richtig sehe?
greetz, Swordfish
-
// ^^ Sag' mal, warum kann ich mir den Rückgabetyp sparen?
Da es ja keine Definition des Operators gibt, ist der Rückgabetyp völlig egal (der für die Signatur einer Funktion/Methode/Operator ja keine Rolle spielt).
Somit hängt die Singleton-Eigenschaft nur noch von einem privatem dtor des "Clients" (<-ist die Bezeichnung möglich?) ab, wenn ich das richtig sehe?
Der Destruktor ist deshalb privat, damit eben von außen kein Objekt dieser Klasse erzeugt werden kann. Deshalb ist aber auch die friend-Anweisung wichtig, damit die Singleton-Klasse als einzige so ein Objekt erzeugen kann.
Und dies ist m.E. auch der Grund, warum es so schwierig ist, eine allgemeingültige Singleton-Template-Klasse zu entwerfen (egal in welcher Richtung: als Basisklasse oder aber umgekehrt). Es gibt immer irgendwelche Regeln, die man beim Entwerfen der "eingebetteten" Klasse beachten muß.
Ich selber mag diese friend-Anweisung nicht...
Bei meiner letzten Firma gab es auch so eine Singleton-Klasse. Dort wurde das dann mithilfe eines Makros "IS_SINGLETON" gelöst...Mal schauen, ob es bald bei Boost so eine Klasse gibt. Aber ich befüchte, es wird ebensowenig angenommen, wie bei dem Pimpl-Vorschlag.
Und nun noch zum letzten Punkt:
Hab' noch den Rückgabetyp von singleton::get_instance( ) geändert
Wenn du direkt T& (statt singleton<T>&) verwendest, dann brauchst du auch nicht mehr von T erben.
Und dies wäre dann eine dritte Möglichkeit einer Singleton-Implementierung (ohne Vererbung)...