Singletons in C++
-
Hallo,
im deutschen Wikipedia-Eintrag zum Singleton-Entwurfsmuster findet man diese Beispielimplementierung eines Singleton-Objekts in C++:
template <class T_DERIVED> class CSingleton { public: static T_DERIVED& GetInstance() { static T_DERIVED oInstance ; return oInstance ; } protected: CSingleton(){} private: CSingleton( const CSingleton& ) ; CSingleton& operator=( const CSingleton& ) ; } ; // Verwendung class CMySingleton : public CSingleton< CMySingleton > { friend class CSingleton< CMySingleton >; private: CMySingleton(){} //... };Daneben ist auch noch die "konventionelle" Implementierung aufgeführt. Ich frage mich, was für Vorteile habe ich durch obige Implementierung gegenüber der Konventionellen? Alle Singletons haben jetzt den gleichen Typ, was aber keinen praktischen Nutzen mit sich bringt, weil die abgeleiteten Klassen dann komplett verschiedene Interfaces haben. Schreibarbeit spart man sich auch nicht wirklich. Ich sehe einfach keine Vorteile! Man erkennt vllt. etwas einfacher, dass es sich um ein Singleton handelt, aber ist das die Mühe Wert?
Worin liegt der Vorteil?
-
sthet doch im wikipedia
-
Bitte zitieren.
-
Erste Methode: hat den Vorteil, dass bei Programmende das Objekt automatisch zerstört wird (und der Destruktor aufgerufen wird). Darüber, wann das passiert, hat man aber keine Kontrolle.weite Methode: Vorteil dieser ist, dass man bei Erzeugung des Objektes ein spezialisiertes Objekt erzeugen kann, man hat also Polymorphie. Außerdem hat man durch Hinzufügen einer statischen Destroy()-Funktion volle Kontrolle darüber, wann das Singleton wieder zerstört wir
-
... und da "sa" offentlichtlich die Tinte ausgegangen ist, zitiere ich mal das hierfür Wesentliche:
Dritte Methode: Bietet eine Basisklasse um auf einfachste Weise eine Klasse als Singleton auszuweisen.
Ich bin allerdings einerseits sicher, dass Singletoni das bereits gelesen hat und andererseits finde ich das als Erklärung auch ein wenig dürftig.
Nach meiner Vermutung kann man sich so halt die Singelton-Eigenschaft "in die Klasse importieren", ohne Weiteres zu tun als abzuleiten und zu "be-friend-en".
Ein Vorteil ggü. der einfachen Vererbung (2. Variante) ist, dass getInstance() bereits die Childklasse zurückliefert - man spart sich also den Cast...
und schließlich hat man kein dynamisches Objekt und spart sich damit Laufzeit und Pointersemantik.Gruß,
Simon2.
-
Singletoni schrieb:
Ich frage mich, was für Vorteile habe ich durch obige Implementierung gegenüber der Konventionellen?
frage ich mich auch. ich würde obige implementierung nicht mehr benutzen. sowas passiert, wenn man voller eifer die grenzen ausloten will und dabei versäumt, es einfach zu halten. und das nennt sich overengeneering.
-
volkard schrieb:
sowas passiert, wenn man voller eifer die grenzen ausloten will und dabei versäumt, es einfach zu halten. und das nennt sich overengeneering.
wieso? was ist an denn ableiten und befrienden grenzwertig oder nicht einfach? die alternative, alle getInstance()s jedesmal selber zu schreiben birgt doch nur zusätzliche fehlerquellen.
gibt es eigentlich einen speziellen namen für singletons, die explizit initialisiert werden müssen (createInstance()), in fällen in denen z.b. die erzeugungsreihenfolge wichtig ist?
-
wieso? was ist an denn ableiten und befrienden grenzwertig oder nicht einfach? die alternative, alle getInstance()s jedesmal selber zu schreiben birgt doch nur zusätzliche fehlerquellen.
ähm. was willst du an
static Foo f; return f;für Fehler einbauen?
gibt es eigentlich einen speziellen namen für singletons, die explizit initialisiert werden müssen (createInstance()), in fällen in denen z.b. die erzeugungsreihenfolge wichtig ist?
ähm, unser bisheriges GetInstance() ist doch in diesem sinne auch ne erzeugungsreiehnfolgendefinierende explizite initialisierung.
vor dem ersten audfruf von GetInstande() lebt dieser singleton noch nicht.
-
volkard schrieb:
ähm, unser bisheriges GetInstance() ist doch in diesem sinne auch ne erzeugungsreiehnfolgendefinierende explizite initialisierung.
vor dem ersten audfruf von GetInstande() lebt dieser singleton noch nicht.der unterschied ist, dass createInstance() nur einmal aufgerufen werden darf und getInstance() keine instanz mehr erzeugt. es gibt dann also eine feste stelle im programm, wo der singleton initialisiert wird, statt es on demand zu machen.
ist das immer noch ein singleton oder gibt es dafür eine speziellere bezeichnung?
-
volkard schrieb:
ähm. was willst du an
static Foo f; return f;für Fehler einbauen?
im Destruktor einen Zugriff auf ein anderes Singleton das auch so zerstört wird.
multiluigi schrieb:
gibt es eigentlich einen speziellen namen für singletons, die explizit initialisiert werden müssen (createInstance()), in fällen in denen z.b. die erzeugungsreihenfolge wichtig ist?
http://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)Wo hast du jetzt createInstance her?
-
multiluigi schrieb:
der unterschied ist, dass createInstance() nur einmal aufgerufen werden darf und getInstance() keine instanz mehr erzeugt.
das würde der benutzer doch eh falsch machen. deswegen schützt man selbstverständlich createInstance() derart, daß es unschädlich ist, wenn ein anderer benutzer es an anderer stelle nochmal aufruft.
außerdem nennt man es gleich noch um zu GetInstance().es gibt dann also eine feste stelle im programm, wo der singleton initialisiert wird, statt es on demand zu machen.
ruf doch einfach an der einen festgelegten stelle GetInstance() auf. da hat doch keiner was dagegen.
ist das immer noch ein singleton oder gibt es dafür eine speziellere bezeichnung?
klar ist das auch ein singleton. aber der hat keinen speziellen namen, weil er in der praxis nicht gesichtet wird. vermurlich würde man das objekt schlicht mit einem globalen zeiger festhalten und auf das wort singleton ganz verzichten. aber wie kriegen wir das automatische löschen hin? am einfachsten, wieder als meyers-singleton, also das bekannte muster mit der lokalen static-variablen.
Foo& createFooInstance() { static Foo foo; return foo; }das habe ich jetzt allein zu dem zweck gemacht, daß das objekt bei programmende automatisch gelöscht wird. also ein atexit()-eintrag angelegt wird und ne kleine funktion dazu. daß createInstance() jetzt sogar ohne streß mehrfach aufgerufen werden kann, war kostenlose zugabe, die ganz von alleine kam.
-
volkard schrieb:
multiluigi schrieb:
der unterschied ist, dass createInstance() nur einmal aufgerufen werden darf und getInstance() keine instanz mehr erzeugt.
das würde der benutzer doch eh falsch machen. deswegen schützt man selbstverständlich createInstance() derart, daß es unschädlich ist, wenn ein anderer benutzer es an anderer stelle nochmal aufruft.
quatsch, wieso? die funktion ist dazu gedacht, einmal an einer passenden stelle aufgerufen zu werden. und der name zeigt auch eindeutig, dass hier etwas erzeugt wird. erneutes aufrufen wäre ein grober fehler, der nicht einfach still ignoriert werden darf.
das soll doch der wesentliche unterschied zum üblichen singleton sein, wo der absolute zeitpunkt der erzeugung egal ist.außerdem nennt man es gleich noch um zu GetInstance().
das erzeugen der instanz und das holen der instanz wären hier aber zwei unterschiedliche dinge, mit absicht.
es gibt dann also eine feste stelle im programm, wo der singleton initialisiert wird, statt es on demand zu machen.
ruf doch einfach an der einen festgelegten stelle GetInstance() auf. da hat doch keiner was dagegen.
siehe oben. erzeugen und holen sind unterschiedliche dinge.
ist das immer noch ein singleton oder gibt es dafür eine speziellere bezeichnung?
klar ist das auch ein singleton. aber der hat keinen speziellen namen, weil er in der praxis nicht gesichtet wird.
ist das so? woher weißt du das?
-
multiluigi schrieb:
ist das so? woher weißt du das?
Anmeldungsdatum: 06.04.2000
er wäre in den vergangenen 9 jahren hier aufgetaucht. die meiste zeit habe ich davon mitgekriegt und keinen solchen singleton gesehen.
außerdem würde ich erwarten, daß alexandrescu in "modern c++ design" seinen 24 erzeugbaren singletons dann noch ein paar hinzugefügt hatte. hat er aber nicht.warum sollte es denn ein fehler sein, createInstance() ein weiteres mal aufzurufen? das nur-einmal-aufrufen kannste doch nur garantieren, wenn der aufruf am anfang der main() steht und du vorher keinen code ausführst. das ist unfug. ein beispiel für einen singleton sei der standarddrucker. irgendwann kommst du auf die idee, einen kostruktor zu schreiben, in dem der drucker verwendet wird und irgendwann machst von diesem typ eine globale variable, und schwupps, wird der drucker benutzt *bevor* die main() gestartet wird, vor crateInstance() und der rechner schmiert ab. das nur-einmal-starten löst GetInstance() gut. und das in-der-richtigen-reihenfolge-starten auch, denn jeder der den drucker braucht, sichert einfach ab, daß der drucker bereits zur verfügung steht, bevor er benutzt wird. wo ist nur das problem?
-
warum sollte es denn ein fehler sein, createInstance() ein weiteres mal aufzurufen?
weil es oft ein fehler ist, bereits initialisierte programmteile nochmal zu initialisieren. ich möchte schon gerne wissen, wenn das passiert.
das nur-einmal-aufrufen kannste doch nur garantieren, wenn der aufruf am anfang der main() steht und du vorher keinen code ausführst. das ist unfug. ein beispiel für einen singleton sei der standarddrucker.
ein beispiel für einen singleton sei eine logging-klasse. die logdatei wird bei der erzeugung des loggers angelegt, eine eventuell bereits vorhandene damit gelöscht.
wenn nun ein destruktor eines globalen objekts etwas ins log schreiben will, ruft er getInstance() vom singleton auf. dummerweise war der singleton schon zerstört, wird jetzt neu angelegt, und die logdatei ist futsch.
mein singleton macht den programmierer statt dessen darauf aufmerksam, dass hier irgendwas nicht passt.es geht mir nicht darum, dass ich alle denkbare fälle mit meinem spezialsingleton erschlagen muss. ich halte es nur in manchen fällen für sehr praktisch, wenn man die zeitpunkte von konstruktion und destruktion selbst festlegen kann, weil der singleton einfach nur während main() existieren soll und außerhalb nicht. wenn es anders sein soll, nehme ich einen andere strategie.
-
@multiluigi
Also wenn du dich für Sonderfälle und ihre Lösung interessierst, dann empfehle ich dir das schon genannte "Modern C++ Design" von Alexandrescu. Er hat da die gängigen Problem (auch die, die du genannt hast) erklärt. (Natürlich mit der Implementierung).
-
multiluigi schrieb:
weil es oft ein fehler ist, bereits initialisierte programmteile nochmal zu initialisieren. ich möchte schon gerne wissen, wenn das passiert.
Die Idee eines Singletons ist aber eine andere.
Klar koennte man soetwas dummes machen wie explizit instanziieren - aber das schafft meistens nur mehr Probleme und bringt keinen Nutzen.Denn der ganze Trick eines Singletons ist ja, dass er einfach da ist. Das wirkliche Problem dass es zu loesen gilt ist die Destruktion. Meyers Singleton ist da eine gute Antwort. Manchmal reicht das nicht, dann braucht man einen Phoenix. Aber damit hat man bereits 99% aller Faelle abgehakt.
ein beispiel für einen singleton sei eine logging-klasse. die logdatei wird bei der erzeugung des loggers angelegt, eine eventuell bereits vorhandene damit gelöscht.
wenn nun ein destruktor eines globalen objekts etwas ins log schreiben will, ruft er getInstance() vom singleton auf. dummerweise war der singleton schon zerstört, wird jetzt neu angelegt, und die logdatei ist futsch.
mein singleton macht den programmierer statt dessen darauf aufmerksam, dass hier irgendwas nicht passt.Designfehler des Loggingstreams.
Du zerstoerst nie eine Log Datei, du rollst sie nur over.Aber, das ganze ist sowieso eine Themenverfehlung, da der Singleton weiss wenn er wiederaufersteht - es ist trivial da eine spezielle Abfrage einzubauen.
Der Meyers Singleton kann ja zB nicht wiederauferstehen...
es geht mir nicht darum, dass ich alle denkbare fälle mit meinem spezialsingleton erschlagen muss. ich halte es nur in manchen fällen für sehr praktisch, wenn man die zeitpunkte von konstruktion und destruktion selbst festlegen kann, weil der singleton einfach nur während main() existieren soll und außerhalb nicht. wenn es anders sein soll, nehme ich einen andere strategie.
nur waehrend main()? Also waehrend der Laufzeit des Programmes?
Wo ist da ein Spezialfall?Wenn du aber den Singleton immer selber erstellen und zerstoeren willst, willkommen in der Welt von RAII. Dann braucht man den ganzen Aufwand von Singletons ja garnicht.
Ein Singleton bietet naemlich noch ein entscheidendes Feature: er wird nur dann erstellt wenn er gebraucht wird.
Und wenn man richtig programmiert, dann packt man den Singleton sowieso in eine Klasse und dann kann man, wenn man spaeter merkt dass ein Meyers Singleton nicht reicht, immer noch eine komplexere Implementierung waehlen. Aber generell immer alles mit Komplexitaet zu erschlagen ist keine gute Idee. Viel besser ist es zu abstrahieren - und dann, wenn noetig - die komplexitaet einbauen.
-
Shade Of Mine schrieb:
Du zerstoerst nie eine Log Datei, du rollst sie nur over.
Was soll den eine Logdatei "over rollen" sein?
-
Frage 10443 schrieb:
Shade Of Mine schrieb:
Du zerstoerst nie eine Log Datei, du rollst sie nur over.
Was soll den eine Logdatei "over rollen" sein?
Keine Ahnung wie man auf deutsch dazu sagt.
Du nimmst die aktuelle Log Datei und verschiebst sie, meistens mit einem Timestamp dann im Namen und erstellst eine neue LogDatei.
zB um Mitternacht nimmst du alle Logdateien und benennst sie von foo.log in foo-2008-01-02.log um und erstellst eine leere foo.log.
So verlierst du keine Logdateien und die Dateien werden dennoch nicht zu gross. Du kannst so auch recht simpel die laenge fuer die eine logdatei aufgehoben werden soll eingrenzen - oder backupen oder sonstwas machen.
-
Shade Of Mine schrieb:
Keine Ahnung wie man auf deutsch dazu sagt.
da sagt man auf deutsch rotaten dazu.
-
Was ist für den Fall, dass man das Singleton bei der Konstruktion zur Laufzeit parametrisieren will? Für den Fall wäre eine separate createInstance-Methode doch zweckmäßig? Ansonsten bliebe vielleicht nocht der Umweg über globale Variablen, was aber auch nicht mehr Sinn macht, denn wenn die nicht gesetzt werden, bevor das Singleton von einem Clienten gebraucht wird, führt es auch zu einem Fehler.
Ein Fall wäre das z.B. wenn ein Singleton einen Zeiger auf ein nicht-singleton Objekt halten will / muss.