Garbage Collection
-
Also statt einen Pointer zu verwenden
MyObject *obj = new MyObject(param1, param2)sollte man lieber direkt Objekte deklarieren in der Art:
MyObject obj(param1, param2)oder eben am Ende selbst aufräumen:
MyObject *obj = new MyObject(param1, param2) [...] delete obj
-
HeXor schrieb:
Also statt einen Pointer zu verwenden
MyObject *obj = new MyObject(param1, param2)sollte man lieber direkt Objekte deklarieren in der Art:
MyObject obj(param1, param2)oder eben am Ende selbst aufräumen:
MyObject *obj = new MyObject(param1, param2) [...] delete objWo ist denn der Unterschied zwischen 1 und 3, außer dass 1 falsch ist, da das delete vergessen wurde?
Um die Frage zu beantworten: Wenn du keine Gründe hast, die dagegen sprechen, dann nimm 2. Wenn du sehr gut begründen kannst, wieso du ein einzelnes Objekt dynamisch anlegen musst (das kann bei gewissen Polymorphiekonstrukten und sehr langlebigen Objekten der Fall sein), dann nimm weder 1 noch 3, denn wie du an dem falschen 1 siehst, kann man bei 3 leicht etwas falsch machen. Und wenn du auch noch Exceptions und andere unerwartete Sprünge in Erwägung ziehst, wird es schnell unmöglich, es richtig zu machen. Narrensichere Alternativen wurden dir in diesem Thread schon haufenweise genannt.
Und falls du mal in die Verlegenheit kommst, dass du mehrere Objekte dynamisch anlegen musst (was sehr oft der Fall ist), dann ist die Antwort niemals new[], denn new[] ist bloß std::vector ohne dessen Narrensicherheit und ohne sonstige Vorteile.
-
Wenn du etwas auf den Heap legst, muss es da wieder weggeschafft werden, wenn du es nicht mehr brauchst, darauf läuft es hinaus. In den meisten Fällen ist es nicht notwendig, Dinge auf den Heap zu legen, d.h.
MyObject obj(param1, param2)ist in den meisten Fällen der einfachste und beste Weg. Wenn etwas seinen Stapelrahmen überleben soll, zu groß für den Stack ist oder aus anderen Gründen auf den Heap muss, solltest du es per RAII irgendwo verankern. In den meisten Fällen ist der einfachste Weg dafür, einen Smart-Pointer zu verwenden, etwa
std::unique_ptr<MyObject> ptr(new MyObject(param1, param2));oder
std::shared_ptr<MyObject> ptr(new MyObject(param1, param2));bei älteren Compilern wahlweise auch std::tr1::foo_ptr oder boost::foo_ptr.
Ein unique_ptr räumt das Objekt, auf das er zeigt, auf, wenn er seinen Gültigkeitsbereich verlässt (also wenn er selbst zerstört wird), shared_ptr ist eine der-letzte-macht-das-Licht-aus-Konstruktion, d.h. wenn mehrere shared_ptr auf das selbe Objekt zeigen, wird es zerstört, wenn der letzte shared_ptr zerstört wird.
Es ist nicht empfehlenswert, die Aufräumarbeiten immer von Hand zu machen, weil es leicht ist, so was zu vergessen - ganz besonders im Zusammenhang mit Exceptions.
-
Naja nehmen wir mal folgendes Minimalbeispiel:
Eine MyClass.hpp file:
Class MyClass { public: //ein Objekt MyObject obj; //ein Pointer MyObject *objPointer; //eine Funktion void foo(); };Eine MyClass.cpp file:
void MyClass::foo() { MyObject tmp(paramt1, param2); obj = tmp; objPointer = new MyObject(param1, param2); [...verarbeitung...] delete objPointer; }Das obj Objekt muss ich ja erst mit tmp initialisieren (oder gibt es hier einen anderen weg?), während ich den Pointer direkt initialisieren kann (aber dann am Ende löschen muss).
Ich verstehe eure Vorschläge, die, wenn ich mein Framework neu aufbauen würde, eine saubere, "narrensichere" Lösung darstellen würden. Da das Framework aber schon relativ groß ist, versuche ich nun einfach erstmal zu verstehen, wie soetwas prinzipiell (einfach) zu lösen ist.
Ist das Minimalbeispiel soweit ok?
-
obj = MyObject(param1, param2);
-
Wieso ist eine Variable die am Anfang der Funktion MyClass::foo einen sinnvollen Wert bekommt und an deren Ende keinen brauchbaren Wert mehr hat ein Member deiner Klasse? Tut mir leid, das Beispieldesign ist so schlecht, da kann ich nicht einmal raten, was du damit zeigen möchtest. Und somit auch keinen Tipp geben, wie es besser werden kann. Jedenfalls ist es so völlig unbrauchbar.
-
Wenn du das Objekt am Ende der Methode wieder zerstörst, warum willst du es dann in eine Membervariable stopfen?
-
HeXor schrieb:
Aufgrund eines auftretenden Memory Leaks [...] beschäftige ich mich im Moment ein wenig mehr mit dem Thema Garbage Collection. Was ich leider lernen musste ist, dass C++ keine solche bestizt.
C++ bietet andere Mittel und Wege, die es Dir leicht machen, erst gar keinen "Garbage" zu produzieren, der dann weggeräumt werden müsste.
HeXor schrieb:
//Eine Klasse class MyObject { [...] int variable; [...] }; //externe variablen MyObject obj; MyObject *objPointer; //Eine Funktion function foo() { //Setzte Variable eines externen Objects obj.variable = 1; //Initialisiere einen EXTERNEN pointer objPointer = new MyObject(); //Initialisiere einen INTERNEN pointer MyObject *anotherObjPointer = new MyObject(); }Sagen wir nun ich habe irgendwo einen loop, in dem ich die Funktion foo() mehrmals aufrufe.
Ich sehe viel "new" und kein "delete". Wenn Die Funktion oft aufgerufen wird, "leakst" du aufgrund beider "new"s, weil es zu keinem das passende delete irendwo gibt.
Denk doch erstmal genau darüber nach, ob Du new überhaupt brauchst. C++ zwingt Dir die Indirektion per Zeiger gar nicht auf. So etwas wie
void foo() { Klasse* ptr = new Klasse(...); ... delete ptr; }ist im Prinzip schon falsch, weil Du hier den Freispeicher bemühst, wo auch der automatische Speicher gereicht hätte:
void foo() { Klasse obj (...); ... // nix delete, und das ist auch gut so }Selbst mit dem delete da oben kann noch einiges schiefgehen, z.B. wenn zwischendurch eine Ausnahme fliegt. Das Problem dabei ist, dass der rohe Zeiger nicht die richtige Semantik hat. Er fühlt sich nicht für das, auf das er zeigt, verantwortlich ist.
-
pyhax schrieb:
obj = MyObject(param1, param2);Genau das hatte ich eben schon probiert:
in *.hpp
fastjet::ClusterSequence _cluster;in *.cpp
_cluster = fastjet::ClusterSequence(_pseudoJets, _jetDef);Fehlermeldung
FastjetTool.cpp:81: error: no match for ‘operator=’ in ‘((FastjetTool*)this)->FastjetTool::_cluster = fastjet::ClusterSequence(((const std::vector<fastjet::PseudoJet, std::allocator<fastjet::PseudoJet> >&) ((const std::vector<fastjet::PseudoJet, std::allocator<fastjet::PseudoJet> >*)(&((FastjetTool*)this)->FastjetTool::_pseudoJets))), ((const fastjet::JetDefinition&)((const fastjet::JetDefinition*)(&((FastjetTool*)this)->FastjetTool::_jetDef))), ((const bool&)((const bool*)(& false))))’ include/fastjet/ClusterSequence.hh:66: note: candidates are: fastjet::ClusterSequence& fastjet::ClusterSequence::operator=(fastjet::ClusterSequence&)SeppJ schrieb:
Wieso ist eine Variable die am Anfang der Funktion MyClass::foo einen sinnvollen Wert bekommt und an deren Ende keinen brauchbaren Wert mehr hat ein Member deiner Klasse? Tut mir leid, das Beispieldesign ist so schlecht, da kann ich nicht einmal raten, was du damit zeigen möchtest. Und somit auch keinen Tipp geben, wie es besser werden kann. Jedenfalls ist es so völlig unbrauchbar.Ja das hatte ich auch eben gesehen, entschuldigung (und trotzdem danke für die nette reaktion...). Vielleicht ist es so besser:
Eine MyClass.hpp file:
Class MyClass { public: //ein Objekt MyObject obj; //ein Pointer MyObject *objPointer; //eine Funktion void foo(); void finalize(); };Eine MyClass.cpp file:
void MyClass::foo() { MyObject tmp(paramt1, param2); obj = tmp; objPointer = new MyObject(param1, param2); [...verarbeitung...] } void MyClass::finalize() { delete objPointer; }Die main
//ohne params und bla, einfach nur main funktion halt main() { MyClass bar; bar.foo(); bar.finalize(); }Der Pointer soll eben dann noch in vielen anderen Funktionen benutzt werden können. Und eigentlich müsste ich ja dann korrekter weise (auch wenn dann in diesem Beispiel das Ende der Programms erreicht ist) den Pointer am Ende mit finalize() aufräumen.
-
HeXor schrieb:
in *.hpp
fastjet::ClusterSequence _cluster;Globale Variable im Header? Bitte nicht.
Davon abgesehen hast du uns nicht den Code gezeigt, der den Fehler erzeugt, sondern nur einen Teil davon. Ohne compilierfähiges minimales Beispiel kann man dir da nicht so recht helfen.Davon abgesehen: Was möchtest du erreichen?
-
Hast du selber einen operator= überladen? Falls ja, dann achte mal auf const-correctness.
-
pumuckl schrieb:
HeXor schrieb:
in *.hpp
fastjet::ClusterSequence _cluster;Globale Variable im Header? Bitte nicht.
Davon abgesehen hast du uns nicht den Code gezeigt, der den Fehler erzeugt, sondern nur einen Teil davon. Ohne compilierfähiges minimales Beispiel kann man dir da nicht so recht helfen.Davon abgesehen: Was möchtest du erreichen?
Das ist eine Member Variable meiner Klasse in dessen Header file, also eingebettet in
class MyTool { public: fastjet::ClusterSequence _cluster; }
-
HeXor schrieb:
Das ist eine Member Variable meiner Klasse in dessen Header file, also eingebettet in
Merkst was? Anhand deiner bisherigen Codefetzen kann man nichtmal annähernd erahnen, was du wirklich machst, geschweige denn dass man die Ursache des Fehlers erkennt.
pumuckl schrieb:
Ohne compilierfähiges minimales Beispiel kann man dir da nicht so recht helfen.
Davon abgesehen: Was möchtest du erreichen?
-
pumuckl schrieb:
HeXor schrieb:
Das ist eine Member Variable meiner Klasse in dessen Header file, also eingebettet in
Merkst was? Anhand deiner bisherigen Codefetzen kann man nichtmal annähernd erahnen, was du wirklich machst, geschweige denn dass man die Ursache des Fehlers erkennt.
pumuckl schrieb:
Ohne compilierfähiges minimales Beispiel kann man dir da nicht so recht helfen.
Davon abgesehen: Was möchtest du erreichen?
Ich hatte in den vorherigen Beispielen sehr genau klar gemacht was ich wollte, eine Member Variable eienr Klasse initialisieren (siehe meine vorherigen posts) sodass ich dachte jetzt den ganzen kram drum herum weg lassen zu können...
Ein Minimalbeispiel habe ich soweit ich konnte gegeben, den eigentlichen Fehler in einem Minimalbeispiel zu reproduzieren ist schwierig, da ich hier einige Klassen importiere (fastjet klasse) mit weiteren abhängigkeiten, da sich dieses wie gesagt in einem größeren Framework befindet.
Ich habe versucht diese Implementierung:
Deklaration als Membervariable in *.hpp: fastjet::ClusterSquenece *_cluster; Implementation_1 in Funktion der Klasse in *.cpp: _cluster = new fastjet::ClusterSequence(_pseudoJets, _jetDef);Auf eine OHNE Pointer umzubauen:
Deklaration als Membervariable in *.hpp: fastjet::ClusterSquenece _cluster; Implementation_1 in Funktion der Klasse in *.cpp: _cluster = fastjet::ClusterSequence(_pseudoJets, _jetDef);Und bekomme die obige Fehlermeldung. Folgendes funktioniert:
Deklaration als Membervariable in *.hpp: fastjet::ClusterSquenece _cluster; Implementation_1 in Funktion der Klasse in *.cpp: fastjet::ClusterSequence tmp(_pseudoJets, _jetDef); _cluster = tmp;Hoffe das ist verständlich, wenn nicht lasst es mich wissen...
-
Aus der Fehlermeldung lese ich, dass du einen
ClusterSequence& operator=(ClusterSequence&);in der Klasse fastjet::ClusterSequence deklariert hast. Temporäre Objekte lassen sich nicht an non-const-Referenzen binden, und der benutzerdefinierte operator= verhindert die Generation eines Default-Operators.
Was du eigentlich willst, ist ein
ClusterSequence& operator=(ClusterSequence const &);Dass Objekte in C++ keine Referenztypen sind, ist dir bekannt?
Ich vermute aber, dass das grundlegende Problem tiefer geht und du eigentlich keine Funktionen foo und finalize willst, sondern Konstruktoren und Destruktoren. "finalize" ist in C++ kein spezieller Funktionsname wie in Java, und das Hantieren mit halbfertigen Objekten ist problematisch - letzteres gilt nicht nur für C++.
Kann es sein, dass du eigentlich
class MyClass { public: MyClass(foo_t const &pseudo_jets, bar_t const &jet_defs); ~MyClass(); private: fastjet::ClusterSequence cluster_; }; MyClass:MyClass(foo_t const &pseudo_jets, bar_t const &jet_defs) : cluster_(pseudo_jets, jet_defs) { } MyClass::~MyClass() { // Inhalt von "finalize" stattdessen hier }willst, um
MyClass obj(pseudo_jets, jet_defs);schreiben zu können? Manuelle Aufräumarbeiten sind dann nicht mehr notwendig, weil
{ MyClass obj(foo, bar); ... } // MyClass::~MyClass() hier ausgeführt und der von obj belegte Speicher freigegeben wird.
-
HeXor schrieb:
Fehlermeldung
FastjetTool.cpp:81: error: no match for ‘operator=’ in ‘((FastjetTool*)this)->FastjetTool::_cluster = fastjet::ClusterSequence(((const std::vector<fastjet::PseudoJet, std::allocator<fastjet::PseudoJet> >&) ((const std::vector<fastjet::PseudoJet, std::allocator<fastjet::PseudoJet> >*)(&((FastjetTool*)this)->FastjetTool::_pseudoJets))), ((const fastjet::JetDefinition&)((const fastjet::JetDefinition*)(&((FastjetTool*)this)->FastjetTool::_jetDef))), ((const bool&)((const bool*)(& false))))’ include/fastjet/ClusterSequence.hh:66: note: candidates are: fastjet::ClusterSequence& fastjet::ClusterSequence::operator=(fastjet::ClusterSequence&)Der Compiler sagt dir, dass dein Kopier-Konstruktor die falsche Signatur hat. Du musst den Parameter als const kennzeichnen. Die übliche Signatur sieht daher so aus.
T& operator = (T const&)Damit gibts du dem Aufrufer die Garantie, dass du das Objekt nicht veränderst. In Kombination mit const-Memberfunktionen, die du vermutlich noch nicht kennst, ist das sehr sinnvoll.
HeXor schrieb:
Ja das hatte ich auch eben gesehen, entschuldigung (und trotzdem danke für die nette reaktion...). Vielleicht ist es so besser:
Eine MyClass.hpp file:
Class MyClass { public: //ein Objekt MyObject obj; //ein Pointer MyObject *objPointer; //eine Funktion void foo(); void finalize(); };Eine MyClass.cpp file:
void MyClass::foo() { MyObject tmp(paramt1, param2); obj = tmp; objPointer = new MyObject(param1, param2); [...verarbeitung...] } void MyClass::finalize() { delete objPointer; }C++ hat, im Gegensatz zu Java (C++ <3) Destruktoren, die bei Zerstörung automatisch aufgerufen werden. Mit der Suchmaschine deiner Wahl (Als ob jemand was anderes als Google verwenden würde) findest du dazu bestimmt was.
Abgesehen davon ist der Zeiger immer noch nicht notwendig.
HeXor schrieb:
Die main
//ohne params und bla, einfach nur main funktion halt main() { MyClass bar; bar.foo(); bar.finalize(); }main muss in C++ int zurückgeben. Allerdings ist es in der main erlaubt, kein return hinzuschreiben, in diesem Fall wird 0 zurückgegeben.
HeXor schrieb:
Der Pointer soll eben dann noch in vielen anderen Funktionen benutzt werden können.
Mit diesem Satz fange ich nichts an.
HeXor schrieb:
Und eigentlich müsste ich ja dann korrekter weise (auch wenn dann in diesem Beispiel das Ende der Programms erreicht ist) den Pointer am Ende mit finalize() aufräumen.
Nimm die oben von mir angesprochenen Destruktoren, dann musst du gar nichts machen.
P.S.: Der "fehlende" GC von C++ fehlt überhaupt nicht. Es ist so gewollt, dass C++ keinen hat. Mag sein, dass das für dich als Javaner erstmal total veraltet rüberkommt, allerdings hat C++ mit RAII einen sehr guten, meiner Meinung nach allen anderen Sprachen überlegenen Mechanismus fürs Resourcen-Management. In Java fällt sowas schnell mal unter den Tisch, von den ganzen hässlichen try-catch-finally Blöcken möchte ich gar nicht erst reden.
Es stimmt auch, dass GCs einige Probleme von manueller Speicherverwaltung (Fragmentierung, Zyklische Referenzen, ...) auf eine für den Programmierer einfache Weise lösen können. Letzteres kann jedoch bereits durch die Verwendung von weak_ptr umgangen werden, und gegen Fragmentierung hilft ein eigenen Allokator mit speziellen Smartpointern, die es erlauben, den Speicher auch mal rumzuschieben. Alles nicht sehr einfach, aber machbar.
P.P.S.: Ich vermute mal, dass dein Projekt mehrere tausend Zeilen Code hat, die du nun erstmal korrigieren musst. Ich fände es zwar besser, wenn du das tun würdest, allerdings gibt es auch für C++ GCs. Ein Beispiel ist der Hans Boehm GC.
-
@seldon
Im prinzip ist dein Vorschlag richtig, allerdings kann ich leider nicht beim Definieren des MyClass Objekts direkt die _cluster variable mittels Übergabe der beiden Parameter definieren, weil die beiden Parameter ebenfalls interne Parameter der Klasse sind, welche erst noch (durch zwei Funktionen mit komplexerem Inhalt) definiert werden müssen.
Deshalb muss ich an einem Punkt in der Klasse diese Variable definieren. Um mich von Pointern zu entfernen wollte ich dieses in der *.hpp als Klassen Member Variable deklarierte Objekt _cluster in einer der Funktionen in der *.cpp also mittels
_cluster = fastjet::ClusterSequence(param1,param2);definieren, was aber mit der obigen Fehlermeldung fehlgeschlagen ist. Die Implementierung hinter diesem Objekt ist ein externes Tool welches ich einbinde, und zweckes updates leider nicht verändern kann/sollte. Wenn also wie es scheint in diesem Tool der "=" Operator überladen wurde, bleibt mir nichts anderes übrig als den Weg über eine temporäre Variable zu wählen
fastjet::ClusterSequence tmp(param1,param2); _cluster = tmpOder seht ihr einen anderen Weg?
-
314159265358979 schrieb:
Die übliche Signatur sieht daher so aus.
T& operator = (T const&)Damit gibts du dem Aufrufer die Garantie, dass du das Objekt nicht veränderst. In Kombination mit const-Memberfunktionen, die du vermutlich noch nicht kennst, ist das sehr sinnvoll.
Das ist gerade nicht der Grund, siehe Seldons Post eins vor deinem. BTW. kann man const-Memberfunktionen auch problemlos bei non-const-Objekten verwenden. Andersrum (non-const-Methoden auf const-Objekte) gehts halt nicht.
-
314159265358979 schrieb:
HeXor schrieb:
Der Pointer soll eben dann noch in vielen anderen Funktionen benutzt werden können.
Mit diesem Satz fange ich nichts an.
^^ Du hast aber schon was damit angefangen - darauf geantwortet

-
Es gibt einfach zu wenig brauchbare Informationen. Sorry, aber so kann man dir leider nicht helfen.
Von dem was ich bislang aber erahnen kann vermute ich, dass dein jetziges Problem eine unselige Aneinanderreihung von vielen anderen Dingen ist, die man in C++ eben anders als in Java löst. Ich glaube nicht, dass sich da noch viel retten lässt.