Speicherfreigabe bei statischen Elementen?
-
Hallo zusammen,
ich habe ein paar Fragen zu meinem alten Thread von hier: http://www.c-plusplus.net/forum/viewtopic-var-t-is-183222-and-highlight-is-.html.
In meinem Beispiel:
//header class FrameFactoryC { public: static void createAddFrame(...); static const std::vector<FrameC *> &getFrames(const int type); private: static std::map<const int, std::vector<FrameC *> > frames; FrameFactoryC(); ~FrameFactoryC(); FrameFactoryC(const FrameFactoryC&); void operator=(const FrameFactoryC&);; }; // Implementierung FrameFactoryC::FrameFactoryC() { } FrameFactoryC::~FrameFactoryC() { for (std::map<const int, std::vector<FrameC *> >::iterator iter = frames.begin(); iter != frames.end(); iter++) { for (std::vector<FrameC *>::iterator itVec = iter->second.begin(); itVec != iter->second.end(); itVec++) { delete (*itVec); } iter->second.clear(); } frames.clear(); } void FrameFactoryC::createAddFrame(const int type, int width, int height, PLANEPTR data, PLANEPTR mask, const struct BitMap * const friendBitMap) { FrameC *frame = new FrameC(width, height, data, mask, friendBitMap); frames[type].push_back(frame); } const std::vector<FrameC *> &FrameFactoryC::getFrames(const int type) { return frames[type]; }habe ich ja den Fehler drin, dass der Speicher der mit
newangelegten FrameC-Objekte im Destruktor der Klasse freigegeben wird. Das funktioniert in meinem Falle allerdings nicht, da die Klasse nie instanziiert wird. Es werden nur die statischen Methoden genutzt.Allerdings wird der Speicher der FrameC-Objekte anscheinend doch freigegeben (zumindest gemessen an dem, was ein Vergleich der freien Speicherbytes vor und nach Programmablauf anzeigt)! Erfolgt hier die Freigabe beim Beenden des Programmes, wenn die statischen Elemente out of scope gehen? Aber wer ruft dann die ganzen
free-Operationen auf?Die gleiche Frage gilt für den Vorschlag des anonymen Namespaces aus dem selben Thread:
//meineFunktionen.cpp namespace { // Anonymer namespace int glob1 = 3; // geht genauso wie meine "Hilfsfunktionen" oben } namespace meineFunktionen { int f(int x) { return x * glob1++; } }Wie gibt man hier den Speicher von Elementen frei, die mit
newangelegt wurden (u.U. sogar anonym) und deren Zeiger sich nur in einem STL-Container befinden, der wie glob1 angelegt ist?
Wann würde der Speicher für den Container selbst freigegeben werden?Vielen Dank schon mal!
Ciao
-
Ich würde sagen, laut meinem Verständnis der Dinge, das static Daten beim Beenden des Programms freigegeben werden... automatisch, denk ich mal.
Jedoch sollt man den Destructor-Code in eine Funktion auslagern wie z.B. deleteAllFrames() welche dann static public ist.
Damit kann man dies dann im Hauptprogramm händisch die Ressourcen freigeben...
Denn diese werden ja nicht gelöscht wenn die static map freigegeben wird.
-
Reth schrieb:
Allerdings wird der Speicher der FrameC-Objekte anscheinend doch freigegeben (zumindest gemessen an dem, was ein Vergleich der freien Speicherbytes vor und nach Programmablauf anzeigt)!
Das liegt am Betriebssystem. Dieses gibt nach Beendigung eines Programms dessen gesamten zugewiesenen Speicher wieder frei. Trotzdem sollte man sich darauf nicht verlassen, es ist kein guter Stil, seinen angeforderten Speicher nicht wieder freizugeben. Ebenso ist in deinem Fall der instanzgebundene Destructor der gänzlich falsche Platz für die Speicherfreigabe. Wenn man in einer Funktion eine lokale Variable der Klasse anlegt, ist nach Beendigung der Funktion plötzlich der statische Frames-Container leer!.
Die einfachste Lösung ist statt der rohen Zeiger die Nutzung von Smart-Pointern oder Pointer-Containern, welche bei ihrer automatischen Zerstörung beim Programmende den Speicher freigeben. Eine eventuell kurzfristig einfachere, aber auch ein bisschen gehackte Lösung kann z.B. so aussehen:class FrameFactoryC { friend class FrameFactoryC::autodeleter; class autodeleter { ~autodeleter() { FramesFactoryC::DeleteAllFrames(); } }; static autodeleter _autodeleter; static void DeleteAllFrames(); // Statische Freigebefunktion // Rest } FrameFactoryC::autodeleter FrameFactoryC::_autodeleter;
-
Die letzte Zeile in Deinem Code-Bsp. hab ich leider nicht ganz kapiert (bin immer noch neu, was C++ anbelangt und tue mich z.T. mit dem Lesen des Sources schwer). Bedeutet die, man legt die statische Instanz vom Typ autodeleter aus FrameFactoryC an, damit beim Beenden des Programms deren Destruktor gerufen werden kann?
Heisst das aber bezogen auf die Empfehlung in meinem durch URL angegebenen Thread auch, dass man im Falle der Nutzung eines anonymen Namespaces für den Container und eines expliziten Namespaces für die Factory-Funktionen dann auch eine Factory-Funktion für das Aufräumen anbietet (wäre mir zumindest mal eingefallen)?
In der Version des Singletons hätte man das Thema ja auch nicht, da man dort im Destruktor den Container leeren und die Objekte freigeben könnte!
-
Reth schrieb:
Die letzte Zeile in Deinem Code-Bsp. hab ich leider nicht ganz kapiert (bin immer noch neu, was C++ anbelangt und tue mich z.T. mit dem Lesen des Sources schwer). Bedeutet die, man legt die statische Instanz vom Typ autodeleter aus FrameFactoryC an, damit beim Beenden des Programms deren Destruktor gerufen werden kann?
Statische Variablen müssen außerhalb der Klasse definiert werden.
class foo { static int i; // Deklaration }; int foo::i = 0; // DefinitionEine Ausnahme sind konstante statische Variablen eines Integraltypens, das trifft hier aber nicht zu.
Reth schrieb:
Heisst das aber bezogen auf die Empfehlung in meinem durch URL angegebenen Thread auch, dass man im Falle der Nutzung eines anonymen Namespaces für den Container und eines expliziten Namespaces für die Factory-Funktionen dann auch eine Factory-Funktion für das Aufräumen anbietet (wäre mir zumindest mal eingefallen)?
Ich bin mir jetzt nicht sicher, ob ich die Frage richtig verstanden habe, aber wenn man statische Funktionen anbietet, die Speicher reservieren und dieser Speicher nicht automatisch freigegeben wird, wäre eine statische Funktion zur Speicherfreigabe durch den Anwender wohl das Mindeste.
Reth schrieb:
In der Version des Singletons hätte man das Thema ja auch nicht, da man dort im Destruktor den Container leeren und die Objekte freigeben könnte!
Dann sollten die Container aber auch Membervariablen des Singletons sein.
-
ipsec schrieb:
Statische Variablen müssen außerhalb der Klasse definiert werden.
class foo { static int i; // Deklaration }; int foo::i = 0; // DefinitionEine Ausnahme sind konstante statische Variablen eines Integraltypens, das trifft hier aber nicht zu.
OK, verstanden.
ipsec schrieb:
Reth schrieb:
Heisst das aber bezogen auf die Empfehlung in meinem durch URL angegebenen Thread auch, dass man im Falle der Nutzung eines anonymen Namespaces für den Container und eines expliziten Namespaces für die Factory-Funktionen dann auch eine Factory-Funktion für das Aufräumen anbietet (wäre mir zumindest mal eingefallen)?
Ich bin mir jetzt nicht sicher, ob ich die Frage richtig verstanden habe, aber wenn man statische Funktionen anbietet, die Speicher reservieren und dieser Speicher nicht automatisch freigegeben wird, wäre eine statische Funktion zur Speicherfreigabe durch den Anwender wohl das Mindeste.
Dort ging es nicht um die statischen Versionen, sondern um die Funktionssammlung innerhalb eines Namespaces, die eine gemeinsame Variable eines anonymen Namespaces nutzen. Denke aber, dass dort das Gleiche zutrifft: Man braucht eine Funktion zum Aufräumen und Speicher freigeben. Die Variable würde in meinem Fall einem STL-Container entsprechen, der Zeiger enthält die als einzige für die Freigabe der dahinter liegenden Objekte (diese sind mit new angelegt) verwendet werden können.
ipsec schrieb:
Reth schrieb:
In der Version des Singletons hätte man das Thema ja auch nicht, da man dort im Destruktor den Container leeren und die Objekte freigeben könnte!
Dann sollten die Container aber auch Membervariablen des Singletons sein.
Ja natürlich. Ich frage mich aber immer noch, ob der Singleton-Ansatz für meine Factory/Verwaltung aus dem Thread der bessere Ansatz ist, oder die vorgeschlagene Funktionssammlung mit dem Container im anonymen Namespace?
-
Reth schrieb:
Ich frage mich aber immer noch, ob der Singleton-Ansatz für meine Factory/Verwaltung aus dem Thread der bessere Ansatz ist, oder die vorgeschlagene Funktionssammlung mit dem Container im anonymen Namespace?
Das kommt denk ich auch auf deine konkrete Anwendung an (wie werden die Funktionen benutzt?). Ein Singleton hat aber nicht nur den Vorteil der automatischen Speicherfreigabe, es ist in Hinblick auf Erweiterbarkeit sicher auch die günstigere Lösung. Zumal man bei der Funktionssammlung ja mehr oder weniger mit einer globalen Variable arbeitet und sowas ja vermieden werden sollte. Wenn Daten und Funktionen zusammengehören, sollte man sie zusammen in eine Klasse packen.
-
Das ist richtig. Was den Namespace anbelangt trifft dann noch das Argument der stärkeren Kapselung besteht.
Allerdings bin ich mit meinem bisherigen Design doch sehr unzufrieden aus verschiedenen Gründen!
Was machen möchte ist eine vernünftige Aufteilung für meine Grafikena zu finden. Die Idee ist Folgende:
Es gibt AnimationsObjekte, die verschieden Animationen beinhalten können. Animationen wiederum bestehen aus einzelnen Frames (einzelnes Bild).
Ein Bsp.: Das AnimationsObjekt eines Gebäudes enthält mehrere Animationen für verschiedene Zustände, die das Gebäude haben kann (z.B. "Im Aufbau", "Beschädigt", "Zerstört" etc.). Jede dieser Animationen hat ihre eigenen Frames.Das Design ist bisher so, dass jeder Frame einzeln in der FrameFactory erzeugt wird. Pro Animation, die im AnimationsObjekt erzeugt wird können nun einzelne Frames hinzugefügt werden (in der Reihenfolge, wie sie abgespielt werden sollen).
Bisher wurden Animationen nie aus dem AnimationsObjekt herausgegeben, da sie nur dort verwendet wurden. Sie bekamen bei ihrer Erzeugung gleich einen konstanten Vector mit ihren Frames übergeben. Um dies zu gewährleisten hatte die FrameFactory eine Methode zum Erzeugen und internen Hinzufügen von Frames je nach Frameart (Gebäudeframe, Landschaftsframe, ...). Die FrameFactory konnte nur den konstanten Vector von Frames einer Art zurückgeben, keine einzelnen Frames.
Letzteres hab ich nun geändert, da in einer Animation auch mal gleiche Frames mehrfach vorkommen können sollen (bei der bisherigen Vorgehensweise konnte jeder Frame nur einmal eingefügt werden, oder wurde doppelt angelegt). Nun können Frames einzeln in der FrameFactory erzeugt und als konstante Referenz zurückgegeben werden. Damit ich nun aber Frames einzeln einer Animation hinzufügen kann muss ich demnach entweder die erzeugten Animationen-Objetkte nach außen geben können (bisher nicht nötig), oder aber der Klasse, welche die Animationen-Objekte erzeugt noch Methoden spendieren, mit denen Frames einzelnen Animationen hinzugefügt werden können!Puh! Lange Rede, hoffentlich verständlicher Sinn!
Der zweite Punkt, der mich beim meinem derzeitigen Design stört sind die Parameterlisten, die dadurch entstehen! Bei der Erzeugung der Frames zu einer Animation ändern sich eigentlich nur die Grafikdaten. Größen usw. bleiben gleich. Da aber derzeit eine statische Fabrikfunktion zur Erzeugung benutzt wird, müssen immer alle Parameter mitgegeben werden. Meine Idee dazu wäre, bei einem Singleton eine Methode anzubieten, welche die "Erzeugungsumgebung" initialisiert (Größen usw. werden gesetzt) woraufhin man Frames mit diesen gesetzten Parametern erzeugt, in dem man nur die jeweils neuen Grafikdaten der Frabrikmethode mitgibt.
Allerdings gibt das pro Frameerzeugung immer noch eine Zeile Code und das sieht bei entsprechend vielen Frames irgendwie nicht so toll aus (für meine Begriffe empfinde ich das irgendwie "unelegant!).So, ne Menge Text, aber eigentlich alles nicht mehr zum Thema. Sollte ggf. meinen nächsten "Designthread" damit eröffnen!
-
Reth schrieb:
Der zweite Punkt, der mich beim meinem derzeitigen Design stört sind die Parameterlisten, die dadurch entstehen!
Warum fasst du die Parameter zur Erzeugung eines Objekts nicht in eine eigene Klasse zusammen?
-
Nexus schrieb:
Warum fasst du die Parameter zur Erzeugung eines Objekts nicht in eine eigene Klasse zusammen?
Das wäre auch noch ne Möglichkeit, die ich an anderer Stelle auch schon genutzt habe. Dann bräuchte ich zumindest nur immer einen Parameter in einem Objekt dieser Klasse ändern, um alle dazu passenden Frames zu erzeugen.
Zum Thema lange Initialisierungsblöcke werde ich für eine Verkürzung wohl nicht um eine Auslagerung in Datei(en) herum kommen, um das Ganze dann laden zu können.
-
Das schreit doch geradezu nach einem Singleton...
-
DocShoe schrieb:
Das schreit doch geradezu nach einem Singleton...
Was genau meinst Du? Die Klasse, welche die Frames erzeugt? Da stimme ich Dir zu. Für das Thema Animationen innerhalb der Animationsobjekte und die Übergabe der Frames an die Animationen finde ich aber keine der beiden Ideen besonders toll (weder das Herausgeben der Animationen, um sie mit Frames zu bestücken, noch das Bereitstellen von "Durchreichmethoden" innerhalb der Klasse Animationsobjekte - Letzteres versteckt zumindest weiterhin die Implementierung der Animationen).
Eine andere Frage dazu noch, da ich immer noch Anfänger bin:
Nach meiner Vorstellung erzeuge ich an der Stelle, an der alle Frame-Objekte erzeugt werden ein FrameData-Objekt und übergebe immer eine Kopie in den Konstruktor des Frame-Objektes, so dass der aktuelle Zustand des FrameData-Objektes übernommen wird. Dann ändere ich den Zustand des ursprünglichen FrameData-Objektes und erzeuge ein neues FrameObjekt, dem ich nun eine Kopie mit dem neuen Zustand des FrameData-Objektes übergebe. Der Zustand der Kopie des FrameData-Objektes im zuerst erzeugten Frame-Objekt bleibt davon unberührt. Ist das so richtig?
Hier mal in Form von PseudoCode:
FrameDataC frameData(80, 100, dataPtr1, maskPtr, friendBitMapPtr); FrameC frame1 = FrameFactoryC::createFrame(frameData); // ab hier hat frame1 eine lokale Kopie von frameData frameData.setDataPtr(dataPtr2); FrameC frame2 = FrameFactory::createFrame(frameData); // hier hat nun frame2 eine eigene lokale Kopie von frameData // die lokale Kopie in frame1 bleibt aber mit den für frame1 // übergebenen Werten erhaltenFunktioniert das auch, wenn ich FrameDataC als const-Parameter für createFrame() und lokales Const-Objekt für FrameC vorsehe?