Singleton für std:vector
-
Frankyyy schrieb:
Jetzt möchte ich aber kein Objekt einer eigenen Klasse zurückgeben, sondern
einen std::vector.Wieso? Das Singleton ist dazu da, dass es nur eine Instanz dieser Klasse gibt und alle mit der gleichen Instanz arbeiten. Den std::vector würde ich als private member ins Singelton packen und dann zugriffsfunktionen anbieten.
-
hustbaer schrieb:
Hast du nun andere statisch instanzierte Objekte die auf das Singleton zugreifen... ist das schlecht. Du kannst nämlich die Reihenfolge nicht kontrollieren in der diese Objekte zerstört werden.
Nunja, kontrollieren kann man sie eigentlich schon, zB mittels nifty initialization. Und das hat ein Singleton ja praktisch "builtin". Man muss dabei nur sicherstellen, dass jedes Objekt, welches das Singleton verwendet, bei der Erzeugung dieses veranlasst, zu instanzieren.
hustbaer schrieb:
Damit ist sichergestellt dass "theSingleton" niemals freigegeben wird, und das ist wie gesagt meistens gut so.
Nein, genau das ist es nicht. Jeder dynamisch reservierte Speicherbereich sollte auch wieder freigegeben werden. Alles andere kann eine Kompromittierung des Systems nach sich ziehen. Zu DOS Zeiten konnte man das zB sehr gut mitverfolgen. Moderne Betriebssysteme kaschieren diese Fehler zwar idR, es bleibt trotzdem unsauber.
-
@groovemaster: Äh. Du erzählst mir hier nicht wirklich was neues.
Nein, genau das ist es nicht. Jeder dynamisch reservierte Speicherbereich sollte auch wieder freigegeben werden. Alles andere kann eine Kompromittierung des Systems nach sich ziehen. Zu DOS Zeiten konnte man das zB sehr gut mitverfolgen. Moderne Betriebssysteme kaschieren diese Fehler zwar idR, es bleibt trotzdem unsauber.
Ok. Dann sag mir bitte einen einfachen Weg wie man das Problem mit der Deinitialisierungs-Reihenfolge löst. Betonung auf einfach. Komplizierte Wege die diverse Dinge von Clients des Singleton erfordern kenn ich genug.
-
hustbaer schrieb:
Wenn diese anderen Objekte dann zerstört werden *nachdem* das Singleton bereits zerstört wurde, und der Destruktor eines dieser anderen Objekte über die Referenz auf das nunmehr zerstörte Singleton zugreift ... dann machts eben *bumm* und das ist nicht gut.
Falls das zurückgegebene Objekt von anderen statischen Objekten referenziert wird,
würde ich einen Shared_pointer auf den Vektor zurückgeben.
Aber im Normalfall kann man doch auch dann noch garantieren, dass das statische
Objekt mit der Referenz vorher freigegeben wird.Antwort schreiben schrieb:
Wieso? Das Singleton ist dazu da, dass es nur eine Instanz dieser Klasse gibt und alle mit der gleichen Instanz arbeiten. Den std::vector würde ich als private member ins Singelton packen und dann zugriffsfunktionen anbieten.
Die Kapselung ist aber doch eigentlich unnötig. Welchen Vorteil versprichst du
dir davon? Ob jetzt direkt nur eine Instanz von dem Vector besteht oder von dem
Objekt, das den Vector enthält. Man kann so evtl. einen Shared_pointer
"verstecken", aber mehr doch nicht, oder?
-
Soll das jetzt ein vecotr bestehend irgendwas oder ein vector bestehend aus diesen singleton klassen sein?
Was soll das ganze am Schluss werden, wofür willst du das einsetzen?
-
Ein Singleton-Vektor bestehend aus irgendwelchen Objekten...
Also das Singleton bezogen auf den Vektor, nicht auf die Objekte
darin...
-
Du kannst es ungefähr so machen:
template< typename T > class SingletonVector : public std::vector< T > { public: static SingletonVector& getSingleton() { static SingletonVector instance; return instance; } private: SingletonVector(){} SingletonVector( const SingletonVector& other ); const SingletonVector& operator=( const SingletonVector& other ); }; //SingletonVector< int >::getSingleton().push_back( 3 ); //cout << SingletonVector< int >::getSingleton()[ 0 ];
-
Vieleicht wäre ja das Factorypattern besser, auch wenn ich immer noch nicht weiß was das ganze werden soll. Aber anschauen kannst dus ja mal.
-
hustbaer schrieb:
Ok. Dann sag mir bitte einen einfachen Weg wie man das Problem mit der Deinitialisierungs-Reihenfolge löst.
Hab ich doch bereits getan. Was daran ist denn unklar?
hustbaer schrieb:
Betonung auf einfach.
Das Singleton dazu zu bringen, zu instanzieren, kann man wohl kaum als kompliziert bezeichnen. Das ist maximal ein Funktionsaufruf.
Aber falls du es immer noch nicht verstehst, hier mal ein kleines Beispiel:
class singleton { public: static singleton& instance() { static singleton instance_; return instance_; } void do_something(int x) { } private: singleton() { } singleton(const singleton&); singleton& operator =(const singleton); ~singleton() { } }; class foo { public: void do_something() { singleton::instance().do_something(1); } foo() { } ~foo() { singleton::instance().do_something(2); } }; foo a; int main() { a.do_something(); return 0; }Hier wird das Objekt a vom Typ foo im globalen Namensraum erzeugt, hat also statische Lebensdauer. Erst innerhalb von main, wird durch den Aufruf von a.do_something() das Singleton instanziert. Es wird also zuerst a und dann das Singleton erzeugt. Entsprechend werden diese in umgekehrter Reihenfolge auch wieder zerstört. Warum das so ist, findet man im Standard:
3.6.3-1 schrieb:
Destructors (12.4) for initialized objects of static storage duration (declared at block scope or at namespace scope) are called as a result of returning from main and as a result of calling exit (18.3). These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized.
Genau hier liegt der Hund begraben. Das Singleton wird also zuerst zerstört, danach a. Im dtor von foo wird aber auf das Singleton zugegriffen, welches dann ja nicht mehr existiert. Als Folge kann undefiniertes Verhalten auftreten:
3.6.3-2 schrieb:
If a function contains a local object of static storage duration that has been destroyed and the function is called during the destruction of an object with static storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed local object.
Wir machen nun nichts weiter, als das Singleton im ctor von foo zu instanzieren.
foo() { singleton::instance(); }An dieser Stelle ist garantiert, dass das Singleton zuerst erzeugt wird, und auch entsprechend zuletzt zerstört wird. Damit lassen sich zwar keine zirkulären Abhängigkeiten auflösen, aber dann sollte vermutlich das Design überdacht werden.
-
Direkt von std::vector abzuleiten ist nicht besonders klug. Die STL ist nicht als Basis für eigene Klassen gedacht. Stostroup und die Entwickler der STL raten davon heftigst ab.
-
Besonders, wenn du dein Object als vector löscht, da der Destruktor nicht virtuell ist.
-
MichelRT schrieb:
Direkt von std::vector abzuleiten ist nicht besonders klug. Die STL ist nicht als Basis für eigene Klassen gedacht. Stostroup und die Entwickler der STL raten davon heftigst ab.
Spielt im konkreten Fall aber keine Rolle.
Cpp-Anfänger schrieb:
Besonders, wenn du dein Object als vector löscht, da der Destruktor nicht virtuell ist.
dito.