Seite 1 von 6
[C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:04
von Brainsmith
Hi, ich würde gerne einen statischen Singleton erzeugen. Das klappt auch alles soweit mit folgendem
Makro:
Code: Alles auswählen
#define DEF_SINGLETON( NAME ) \
public: \
static NAME* Get_Instance() \
{ \
static NAME _instance;\
return &_instance;\
}\
private:\
NAME() {}; \
NAME( const NAME& ); \
NAME & operator = (const NAME &);
Dadurch wird das Objekt auf dem Stack und nicht auf dem Heap angelegt. Ich kenne mich mit Stacks/Heaps nicht so gut aus.
Wenn ich nun den klasseneigenen Destruktor (in dem ich alle Pointer etc lösche) aufrufe, stürzt das Programm ab (klar, weil ich nicht vom Stack löschen kann).
Meine Vermutung wäre, dass der Stack nach dem Programmablauf geleert wird und dementsprechend auch der Speicher. Dementsprechend müsste ich mich nicht um Deallokierung des Speichers kümmern. Ist das soweit richtig? kann ich mir bei einer Sprache wie C++ kaum vorstellen.
Als Notlösung könnte ich den Singleton auf dem Heap anlegen, was ich aber nicht möchte, weil ich die Klasse von Anfang bis Ende der Main-Methode brauche.
Daher die Frage: Wie werde ich den allokierten Speicher meiner Singletonklasse los? =D
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:10
von CodingCat
Du musst überhaupt gar nichts tun, der Witz an der statischen "lokalen" Instanz-Variable ist gerade, dass die Instanz beim ersten Aufruf konstruiert, und beim Programmende automatisch zerstört (inkl. Destruktoraufruf) und freigegeben wird. Im übrigen könntest du dir überlegen, eine Referenz statt eines Zeigers zurückzugeben, um zu signalisieren, dass die Rückgabe immer gültig ist.
Beim zweiten Lesen fällt mir auf, dass du da offenbar noch einige weitere Missverständnisse hast: Manuelle Freigabe ist immer NUR GENAU DANN erforderlich, wenn du auch manuell alloziierst. Das heißt delete NUR für Objekte, welche mit new erzeugt wurden, delete[] NUR für Objektarrays, welche mit new[] erzeugt wurden und free NUR für Speicher, welcher mit malloc angefordert wurde (malloc sollte in alltäglichem C++ aber praktisch nie vorkommen). Ob Heap / Stack ist dabei vollkommen irrelevant und ein Implementierungsdetail der Sprache. Im übrigen landen statische Variablen auch nicht einfach so auf dem Stack, dort wären sie nämlich mit dem Ende der ablaufenden Funktion sofort wieder verschwunden.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:20
von Brainsmith
Danke für die fixe Antwort.
Kann diese Klasse also keine Memory leaks produzieren?
Angenommen ich schreibe im Konstruktor:
nehmen wir mal weiterhin an, dass ich das nicht lösche. Wird der Memory Leak mit dem Löschen meiner Klasse vom Stack auch behoben?
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:22
von CodingCat
Ich hab meine Antwort eben noch ziemlich erweitert, hast du den neuen Teil schon gelesen? Daraus geht auch hervor, dass du in dem neuen Beispiel sehr wohl ein Memory Leak verursachst. Wenn du im Konstruktor mittels new Objekte anlegst, hast du diese im Destruktor mittels delete wieder freizugeben. Lediglich der Destruktor der Singleton wird entsprechend meiner ersten Antwort automatisch aufgerufen, weil die Singleton-Instanz in einer statischen Variable liegt und NICHT mittels new konstruiert wurde.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:33
von Brainsmith
Mir geht es um folgendes: Ich habe meinen statischen Singleton. Innerhalb davon erzeuge ich Objekte mit "new". Wenn ich die mit "new" erzeugten Objekte im Destruktor löschen will, fliegt mir das Programm um die Ohren, obwohl ich ein SAFE_DELETE - Makro benutze. Daher war ja meine Frage, ob alles, was von dem Singleton allokiert wird, mit seinem Ableben mit gelöscht wird.
Das Löschen im Destruktor ging problemlos bis ich die Klasse zu einem statischen Singleton gemacht habe. Jetzt hat er einen bad pointer tief inmitten der Windows-Aufrufe...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:33
von CodingCat
Brainsmith hat geschrieben:Wenn ich nun den klasseneigenen Destruktor (in dem ich alle Pointer etc lösche) aufrufe, stürzt das Programm ab (klar, weil ich nicht vom Stack löschen kann).
Dass dein Programm abstürzt liegt also vermutlich genau daran, dass du den Destruktor selbst aufrufst. Der Destruktor der Singleton-Klasse wird automatisch aufgerufen, solange die Singleton-Instanz nicht mit
new erzeugt wurde. Wenn du den Destruktor manuell aufrufst, wird der Destruktor zweimal ausgeführt, und es kracht ziemlich sicher. Dennoch musst du innerhalb des Destruktors manuell alloziierte Objekte auch wieder manuell freigeben.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:38
von Brainsmith
Das kann ich ausschließen. Ich rufe den Destruktor nicht manuell auf.
Ich bin sogar soweit gegangen, meinen kompletten Destruktor in eine andere Methode zu verfrachten und die Methode dann manuell aufzurufen. Auch da crasht das Ganze.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 14:48
von CodingCat
Hier ein vollständiges Beispiel:
Code: Alles auswählen
#include <iostream>
class singleton
{
int *p;
singleton()
// Mit new konstruiert
: p(new int(42)) { }
~singleton()
{
// Mit delete zerstört
delete p;
}
public:
static singleton& get()
{
// instance wird automatisch beim ersten Aufruf von get() konstruiert und bei Programmende zerstört
static singleton instance;
return instance;
}
int num() const { return *p; }
};
int main()
{
std::cout << singleton::get().num() << std::endl;
return 0;
}
Wenn dein Programm weiterhin abstürzt, hast du sehr wahrscheinlich ein ganz anderes Problem, z.B. im Destruktor der Klasse des mittels
delete zerstörten Objekts, welcher vor deinem Wechsel zur statischen Instanzvariable möglicherweise einfach nie aufgerufen wurde, weil das Zerstören nicht automatisch ging.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 27.05.2011, 15:14
von Brainsmith
Oh mann.... Ich hab natürlich was falsch gemacht...
Okay.. meine innerhalb des Singletons erzeugten Klassen hatten alle einen const char* als Member und ich hab den geistesabwesend gelöscht.
Natürlich crasht das ganze, wenn ich ihm sowas, wie "blabla.ogg" gebe...
Danke für die Hilfe.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 12:41
von BeRsErKeR
Brainsmith hat geschrieben:Code: Alles auswählen
#define DEF_SINGLETON( NAME ) \
public: \
static NAME* Get_Instance() \
{ \
static NAME _instance;\
return &_instance;\
}\
private:\
NAME() {}; \
NAME( const NAME& ); \
NAME & operator = (const NAME &);
Oh mein Gott. Bitte mach das weg. Das ist böse böse böse. Makros mögen ihre Daseinsberechtigung haben aber doch bitte nicht für solche Grausamkeiten.
Ansonsten ist mir noch aufgefallen, dass du von einem SAFE_DELETE-Makro sprachst. Davon sind mir 2 Varianten bekannt.
1. Variante:
Code: Alles auswählen
#define SAFE_DELETE(a) if( (a) != NULL ) delete (a); (a) = NULL;
Falls du diese meinst, dann lösch sie bitte sofort, da sie absolut sinnfrei ist oder ändere sie zu Variante 2.
2. Variante:
Diese macht schon Sinn, aber in den meisten Fällen ist sie im Destruktor der übergeordneten Klasse auch nicht nötig.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 12:50
von Krishty
Ja, richtig. Der C++-Standard schreibt vor, dass delete auch mit nullptr umgehen können muss, darum muss man da nichts prüfen. Und da man das meist im D’tor braucht, nach dem die Lebenszeit eines Objekts sowieso endet und der Zeiger für immer weg ist, braucht man auch nur selten nullen. Trotzdem noch ein Verbesserungsvorschlag:
#define SAFE_DELETE(a) delete (a), (a) = NULL
Komma statt Semikolon, oder wahlweise auch geschweifte Klammern drum. Sonst richtet
if(Bedingung)
SAFE_DELETE(ptr);
ein Massaker an, weil nur das vor dem Semikolon in dem if landet und das Nullen immer ausgeführt wird. Und das Semikolon am Ende weglassen, damit man auch im Quelltext gezwungen ist, ein Semikolon dahinterzuschreiben. Eigentlich ein Musterbeispiel dafür, warum man keine Makros benutzen sollte. Das SAFE im Namen kann nur ironisch gemeint gewesen sein, und wer’s eingeführt hat, lacht sich noch heute drüber kaputt.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 12:55
von dot
Wenn dann besser so:
Code: Alles auswählen
template <typename T>
void SafeDelete(T*& ptr)
{
delete ptr;
ptr = 0;
}
Wobei Krishty ja schon begründet hat warum man das eigentlich sowieso nicht braucht.
Abgesehen davon seit vielleicht auch noch erwähnt dass Singleton ein Antipattern ist den du lieber nicht verwenden willst. Ich hab zumindest in meinem ganzen Leben noch keinen richtig eingesetzten Singleton gesehen.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 13:05
von BeRsErKeR
Krishty hat geschrieben:Ja, richtig. Der C++-Standard schreibt vor, dass delete auch mit nullptr umgehen können muss, darum muss man da nichts prüfen. Und da man das meist im D’tor braucht, nach dem die Lebenszeit eines Objekts sowieso endet und der Zeiger für immer weg ist, braucht man auch nur selten nullen. Trotzdem noch ein Verbesserungsvorschlag:
#define SAFE_DELETE(a) delete (a), (a) = NULL
Komma statt Semikolon, oder wahlweise auch geschweifte Klammern drum. Sonst richtet
if(Bedingung)
SAFE_DELETE(ptr);
ein Massaker an, weil nur das vor dem Semikolon in dem if landet und das Nullen immer ausgeführt wird. Und das Semikolon am Ende weglassen, damit man auch im Quelltext gezwungen ist, ein Semikolon dahinterzuschreiben. Eigentlich ein Musterbeispiel dafür, warum man keine Makros benutzen sollte. Das SAFE im Namen kann nur ironisch gemeint gewesen sein, und wer’s eingeführt hat, lacht sich noch heute drüber kaputt.
Nur noch ein zusätzlicher Hinweis: Falls man geschweifte Klammern haben möchte und das Semikolon am Ende erzwingen will kann man auch folgendes nutzen:
#define NOT_AS_SAFE_AS_YOU_THINK_DELETE(a) do \
{ \
delete (a); \
(a) = NULL; \
} while(0)
Bin aber im allgemeinen kein Freund von Makros, abgesehen vielleicht von Versions-Makros mit ## und/oder #.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 14:50
von CodingCat
Das wird ja immer grausliger, wenn überhaupt eine derartige Konstruktion, dann doch dots Inline-Funktion. :P Dieser ganze
SAFE_*-Blödsinn kommt im übrigen mitunter aus dem DirectX-SDK, dort jedoch als
SAFE_RELEASE(pCOMObj) im Zusammenhang mit
pCOMObj->Release() zu finden. In diesem Fall ist die Überprüfung tatsächlich notwendig, wenn man seinen Objektzustand so schludrig "durchdacht" hat, dass nicht klar ist, ob denn ein
nullptr zu erwarten ist. Auch dies lässt sich jedoch wesentlich sicherer in einer entsprechende Inline-Funktion formulieren.
Zum Singleton-Makro: Wie dot sagt, sind Singletons fast nie die richtige Lösung,
entsprechend ist das Makro für die 2 Singletons, die am Ende maximal vorkommen sollten, absolut überflüssig, es macht den Code nur unlesbar und noch fehleranfälliger.
Ich habe hier selbst Singletons und globale Instanzen vermengt, eine ausführliche Diskussion mit Beispielen und Vergleich findet sich
gegen Ende dieses Threads.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:13
von dot
CodingCat hat geschrieben:Zum Singleton-Makro: Wie dot sagt, sind Singletons fast nie die richtige Lösung, entsprechend ist das Makro für die 2 Singletons, die am Ende maximal vorkommen sollten, absolut überflüssig, es macht den Code nur unlesbar und noch fehleranfälliger.
Ich würde soweit gehen zu sagen zwei Singletons am Ende sind genau zwei Singletons zuviel ;)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:19
von CodingCat
Da magst du in den meisten Fällen Recht haben, ich persönlich habe aber z.B. absolut keinen Bock a) zentrale Log-Einrichtungen und b) zentrale Dateisystem-bezogene Daten überall per Parameter oder gar Members zu verteilen. ;-)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:23
von dot
Ich hätte mich schon einmal entgegen all meiner Prinzipien fast dazu hinreißen lassen genau solche Dinge als Singleton anzulegen. Du glaubst gar nicht wie glücklich ich nach ein paar Monaten war dass ich das doch nicht getan hab ;)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:24
von CodingCat
Na dann, ich bin jedenfalls seit Jahren glücklich damit. :P Viel interessanter wäre aber, wie du es denn am Ende stattdessen gemacht hast. ;)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:26
von dot
Na so wie mans Eben macht: Wenn ein Objekt ein andres benötigt bekommts ne Referenz drauf ;)
Das ist genau Problem mit Singleton: Es wird ständig als globaler Zugriffspunkt auf ein Objekt missbraucht. Genau das ist aber nicht der Sinn von Singleton, steht sogar explizit so im GoF Buch. Leider ist das der wohl am meisten missverstandene Satz der Softwareentwicklung...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:28
von CodingCat
Iih, das kostet dann ja für jedes Objekt im ganzen Programm extra Speicher... ;) Ich weiß, dass das nicht der eigentliche Sinn von Singletons ist.
Ich kann es ja ab sofort "globales Singleton" nennen, ein gänzlich neues und innovatives Entwurfsmuster, welches das klassische Singleton mit der klassischen globalen Variable verbindet. :mrgreen:
Nachtrag zum Post untendrunter: Bei mir loggen aber praktisch alle Objekte.
Nachträgliche Korrektur: Auch hier habe ich das Singleton-Muster mit der typischen statischen Klasseninstanz in der entsprechenden klassischen Singleton-Zugriffsmethode vermengt, welche ich eigentlich bereits zu diesem Zeitpunkt nicht länger als Singleton, sondern in einer entsprechenden freien Funktion und ohne Konstruktionsbeschränkung implementiert hatte. Eine ausführliche Diskussion mit Beispielen und Vergleich findet sich
gegen Ende dieses Threads.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:29
von dot
Bei den zwei, drei Objekten die eine Referenz auf das Log brauchen sowas von egal...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:32
von dot
CodingCat hat geschrieben:Nachtrag zum Post untendrunter: Bei mir loggen aber praktisch alle Objekte.
Und das is sinnvoll?
CodingCat hat geschrieben:Ich kann es ja ab sofort "globales Singleton" nennen, ein gänzlich neues und innovatives Entwurfsmuster, welches das klassische Singleton mit der klassischen globalen Variable verbindet. :mrgreen:
Ich denk mehr muss man dazu nichtmehr sagen :P
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:34
von CodingCat
Was soll daran nicht sinnvoll sein? Ich hasse es, wenn Fehler auftreten, ohne dass ich was davon mitbekomme. Ich logge sogar jede Exception noch bevor sie fliegt. ;-) Aber oftmals will man eben keine Exception, und trotzdem was vom Fehler mitbekommen - was bei nicht behandelten Rückgabewerten sonst ja eher die Ausnahme wäre.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:38
von dot
Für sowas verwend ich einen Debugger. Ein Log verwend ich für grobe Diagnose wenn beim Kunden was schiefläuft wo ich nicht direkt Debuggen kann. Da kommen so Sachen rein wie irgendwelche Fehler beim Laden oder was für eine Hardwarekonfiguration der Rechner hat und welche Versionen einzelner Komponenten verwendet werden und das hat mir schon viel Schmerz erspart. Aber alles und überall zu loggen hat sich für mich noch nie als sinnvoll herausgestellt...
CodingCat hat geschrieben:Aber oftmals will man eben keine Exception, und trotzdem was vom Fehler mitbekommen.
assert() ;)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:41
von CodingCat
Naja, das kommt halt sehr auf das Projekt und seine Größe an - mit dem Debugger kriege ich eben doch mehr das mit, was ich mir gerade gezielt ansehe, und lang nicht alles, was gerade schief laufen könnte. Und das Logging kostet mich praktisch nichts, warum also nicht das Maximum an Information mitnehmen. Ja, assert nutze ich auch. Aber nicht für Fehler, die sich prinzipiell ignorieren lassen.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:47
von dot
CodingCat hat geschrieben:Und das Logging kostet mich praktisch nichts, warum also nicht das Maximum an Information mitnehmen.
Weil die wirklich wichtige Information dann im Rest untergeht.
CodingCat hat geschrieben:Ja, assert nutze ich auch. Aber nicht für Fehler, die sich prinzipiell ignorieren lassen.
Gut, die Art von Fehler hatte ich wohl noch nie ;)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 15:49
von CodingCat
Wow, heute komme ich noch zu meinem absoluten Post-Rekord. :P
Weil die wirklich wichtige Information dann im Rest untergeht.
Ich glaube du verstehst mich falsch, ich logge nicht alles was passiert, sondern alles was schief geht, das ist in der Regel nichts. ;-)
Gut, die Art von Fehler hatte ich wohl noch nie.
Du Glücklicher. :)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 16:04
von Krishty
Ein Log ist doch nicht nur für den einen tödlichen Fehler da, der das Programm abschießt, sondern um nachvollziehen zu können, wie es dazu kam. Das Problem am Debugger ist, dass man nur den aktuellen Zustand sieht und alles zuvor geschehene verloren ist. Wenn im Programm erstmal vollkommen andere Daten sind als man für den Lauf erwartet hätte (was ja nicht heißt, dass sie beim Laden ungültig waren oder man den Fehler anderweitig früher hätte erkennen können!) und man nicht geloggt hat, dass das System seine ganzen Caches erneuert hat und komplett andere Daten durchgegangen ist als es sollte weil man einen falschen Login eingetippt hat, kann man die gesamte Ausführung im Debugger durchsteppen. Je größer die Fehlertoleranz eines Systems (die eine Anforderung sein kann!), desto schlimmer wird es.
Auf ein Log kann ich, und imo jeder, genauso wenig verzichten wie auf Ausnahmen oder assert. Es gibt halt nicht den Fehler an sich, von dem ihr hier redet. In Flugzeugen verzichtet man auch nicht mit dem Verweis auf die hohe Zuverlässigkeit und Testabdeckung auf die Black Box (oder nimmt damit nur den Start auf „damit keine wirklich wichtige Information im Rest untergeht“).
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 16:28
von dot
Schon klar, aber ich hatte noch nie soviele Objekte die etwas loggen müssen dass eine globale Variable für das Log notwendig geworden wäre um den Speicherverbrauch durch die Dependency Injection zu sparen oO
Es ist jedenfalls sicherlich nicht so dass jedes einzelne kleine Sprite was loggen muss. Wenn dann vielleicht die Klasse die die Sprites erzeugt...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.06.2011, 16:32
von Krishty
Ich ganz persönlich hatte auch noch nie so viele unterschiedliche Logs, dass ich mehr als eine globale Variable bzw. Funktion gebraucht hätte. (Jetzt dürft ihr mich stiefeln)
Halt jedem das Seine, oder jeder das Ihrige, oder allen das Essig.
dot hat geschrieben:Es ist jedenfalls sicherlich nicht so dass jedes einzelne kleine Sprite was loggen muss. Wenn dann vielleicht die Klasse die die Sprites erzeugt...
Imo muss man Fehler loggen, sobald sie auftreten. Das bedeutet, dass jede Ausnahme beim Werfen eine Referenz auf den Logger braucht. Kann also bei der Sprite was schiefgehen, braucht sie ihn meiner Einschätzung nach durchaus.