Garbage Collection
-
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.
-
otze schrieb:
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.
Das ist, glaube ich, die wichtigste Aussage, die bisher in diesem Thread gefallen ist. Dem kann ich mich nur anschließen.
-
314159265358979 schrieb:
otze schrieb:
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.
Das ist, glaube ich, die wichtigste Aussage, die bisher in diesem Thread gefallen ist. Dem kann ich mich nur anschließen.
Wer mir nicht helfen will muss es ja auch nicht tun, aber dann auch nicht solche Kommentare ablassen...
Ich kann euch gerne meine ca. 25 files mit ca. 200-2000 Zeilen Code pro file schicken, wenn ihr dann genug Informationen habt.
Ich denke ich habe es mittlerweile mehr als deutlich gemacht.
Eine Klasse MyClass mit eine Member Variable _cluster, welche ich in einer Funktion foo() der Klasse MyClass initialisieren muss, damit sie auch in anderen Funktionen bla(), muh(), bar() der Klasse MyClass zur Verfügung steht. Eine definition beim definieren des MyClass objekts ist aus den beschriebenen Gründen nicht möglich, da erst innerhalb der MyClass Klasse einige Variablen dafür berechnet/definiert werden müssen. Um pointer zu vermeiden soll eine direkt Definition her, welche hier aber nicht mit _cluster = ??? realisierbar ist, da das zu definierende Objekt von einem importierten Tool deklariert wird, welches ich nicht verändern kann und welches offensichtlich den "=" Operator überlädt, verwendet werden. Daher brauche ich einen anderen Weg um dieses (ja möglichst ohne Pointer) zu realisieren, welche ich durch dem temporären Weg (der übrigens funktioniert, aber nicht schön aussieht) realisiert habe.
Ich danke allen die mir versuchen zu helfen, aber kommentare wie "dir ist nicht zu helfen" helfen eben nicht. Und scheinbar sehen andere hier keinen hoffnungslosen Fall sondern äußern gute Vorschläge. Es ist schade wenn man in einem neuen Board eine Hoffnung sieht und sich anmeldet und dann im ersten Thread solche Kommentare bekommnt, obwohl man VERSUCHT ein Minimalbeispiel zu basteln und Informatioen zu geben ohne 20.000 Zeilen code zu posten, wie es viele ander Anfänger selbstverständlich tun. Danke.
-
HeXor schrieb:
Ich danke allen die mir versuchen zu helfen, aber kommentare wie "dir ist nicht zu helfen" helfen eben nicht. Und scheinbar sehen andere hier keinen hoffnungslosen Fall sondern äußern gute Vorschläge. Es ist schade wenn man in einem neuen Board eine Hoffnung sieht und sich anmeldet und dann im ersten Thread solche Kommentare bekommnt, obwohl man VERSUCHT ein Minimalbeispiel zu basteln und Informatioen zu geben ohne 20.000 Zeilen code zu posten, wie es viele ander Anfänger selbstverständlich tun. Danke.
Das ist kein Bashen auf den Anfänger, sondern die nüchterne Feststellung, dass dir bei der Art wie du die Fragen stellst, keine nützliche Antwort zu geben ist, außer so Allgemeinfloskeln wie "C++ ist nicht Java" oder "Das ist Scheiße, wie du das jetzt machst". Nützen dir diese Aussagen etwas? Wenn nicht, dann musst du beantwortbare Fragen stellen. Das ist keine böse Absicht, dass dir niemand gute Antworten gibt, es liegt an dir. Und viel schlimmer ist, dass du das nicht einsiehst und dich gleich persönlich angegriffen fühlst, wenn man dich nach drei Seiten nutzloser Spekulation vermehrt auf diesen Umstand hinweist.
-
HeXor schrieb:
da das zu definierende Objekt von einem importierten Tool deklariert wird, welches ich nicht verändern kann und welches offensichtlich den "=" Operator überlädt, verwendet werden.
Der operator= wird nicht von einem Objekt überladen - das wird in einer Klasse erledigt.
Und wenn du diese Klasse nicht verändern kannst (da "externes Tool", wie du sagst) ist dir leider auch nicht zu helfen. Entweder du schaffst es, dass das Problem im nächsten Release des Tools beseitigt wird, oder du lebst mit dem Workaround mit dem temporären Objekt.
-
Ich hab mal ein bisschen nachgeforscht. Handelt es sich um diese Bibliothek?
Ich kann darin zumindest in der neuesten Version keine solche Überladung finden. Ich vermute, dass einer der Member von ClusterSequence keinen assignment-Operator hat, der ein konstantes Quellobjekt annimmt (im Zweifel fastjet::SharedPtr); dann kann der Compiler für ClusterSequence keinen operator=(ClusterSequence const &) generieren.
Ich weiß aus dem Stand allerdings jetzt nicht, ob der Compiler in solchen Fällen einen operator=(ClusterSequence&) generieren sollte. Könnte eine gcc-Erweiterung sein.
Am besten wäre vom Design her natürlich, wenn du keine halbfertigen FastJetTool-Objekte herumreichen müsstest (dann bestünde auch dieses ganze Problem nicht), aber wenn dir dieser Weg nicht offen steht, wäre ein shared_ptrfastjet::ClusterSequence hier m.E. vertretbar. Lokalität scheint hier eh nicht gewahrt zu sein, also meh in Bezug auf cache-misses. Was du allerdings nicht machen solltest, ist einen nackten Zeiger in das Objekt zu stecken.
-
seldon schrieb:
Was du allerdings nicht machen solltest, ist einen nackten Zeiger in das Objekt zu stecken.
Also ist meine temporäre Variante nicht vertretbar mit:
fastjet::ClusterSequence tmp(param1,param2); _cluster = tmp;
-
So, jetzt hab ich das nochmal im Standard nachgeschlagen. Also, der gcc verhält sich da korrekt; wenn ein X &X::operator=(X const &) nicht generiert werden kann, wird (sofern möglich) ein X &X::operator=(X&) generiert. Das wird hier passiert sein.
Vom C++-Standard her ist die temporäre Variante also möglich. Ob sie hier funktioniert, hängt davon ab, ob die Entwickler der ClusterSequence-Klasse sich Gedanken um Kopiersemantik gemacht haben. Die Verwendung eines SharedPtr deutet ein bisschen darauf hin (ohne Kopien wäre der shared-Teil überflüssig), aber ich werde mich hüten, kategorische Aussagen über Bibliotheken zu machen, mit denen ich mich überhaupt nicht auskenne. Werden in der Dokumentation der Bibliothek manchmal ClusterSequence-Objekte kopiert? Falls ja, ist deine Variante unbedenklich und im Zweifel auch besser, als eine zusätzliche Indirektion einzuführen.
-
Was mir gerade gekommen ist, ist die Idee das ganze mit dem Destruktor der Klasse zu lösen.
Also:
Header file *.hpp
class MyClass { public: //Konstruktor und Destruktor MyClass(); ~MyClass(); //Funktionen void foo(); void bar(); //Member variable fastjet::ClusterSequence *_cluster; }Code file *.cpp
MyClass() {} ~MyClass() { delete _cluster; } void MyClass::foo() { _cluster = new ClusterSequence(param1, param2); } void MyClass::bar() { [..processing..] }Main function
int main(int argc, char *argv[]) { MyClass obj; obj.foo(); obj.bar(); return 1; }In diesem Fall also doch wieder einen Pointer verwenden, diesen aber aufzuräumen sobald das Klassen Object zerstört wird, also diesen im Klassen Destruktor löschen.
Zwar bedeutet das wieder manuelle GarbageCollection, stellt aber doch sicherlich eine in sich konsistente Methode dar, welche zusätzlich die unschöne temporäre Methode umgeht!?
-
HeXor schrieb:
Main function
int main(int argc, char *argv[]) { MyClass obj; obj.foo(); obj.bar(); return 1; }Mach mal:
int main(int argc, char *argv[]) { MyClass obj; MyClass obj2(obj); }Tipp: Du solltest den Kopier- und Zuweisungsoperator implementieren oder unschädlich machen
.
-
HeXor schrieb:
Was mir gerade gekommen ist, ist die Idee das ganze mit dem Destruktor der Klasse zu lösen.
Gratuliere. So steht das in jedem Anfängerbuch genau an der Stelle, an der zum ersten Mal die beiden Begriffe Destruktor und manuelle Speicherverwaltung bekannt sind.
Du kannst das wie Generationen vor dir so machen, das zwar fehleranfällig, aber definitiv besser als gar nichts zu machen. Was die anderen hier dir sagen wollen ist, dass man in der modernen C++-Programmierung nicht mehr in jedem Destruktor allerlei Resourcen freigibt, sondern die Resourcenverwaltung an spezialisierte Klassen delegiert. In dem Fall Smart-Pointer (shared_ptr etc.)
Zwar bedeutet das wieder manuelle GarbageCollection
Das ist, wenn du den Müll rausbringst. In der Programmierung ist Garbage Collection ein anderes Wort für automatische Speicherverwaltung.
-
HeXor schrieb:
Was mir gerade gekommen ist, ist die Idee das ganze mit dem Destruktor der Klasse zu lösen.
Das wurde dir auf Seite 2 Mitte und Seite 3 erster Post bereits gesagt. Aber schön, dass du jetzt endlich anfängst, C++ zu programmieren. Nun musst du nur noch einen Schritt weiter gehen und sagen: "das kann ich automatisieren" und schon bist du bei smart pointern. oder vektoren wenn du Gruppen von Objekten brauchst. Und dann wirst du nie wieder delete selbst schreiben müssen.
-
Vielleicht hilft es, den gedanklichen Vergleich zu modernem Java zu schlagen:
Mit Java 7 wurde das Interface AutoCloseable eingeführt, das eine Möglichkeit bietet, Aufräumarbeiten (wenn auch auf sybtaktisch etwas holprige Weise) zu automatisieren, etwa Dateien oder Sockets zu schließen:
try ( FileInputStream file = new FileInputStream(filename); ) { // ... } // file wird automatisch geschlossen (file.close() aufgerufen)Dieser Mechanismus kommt ursprünglich von C++ und ist da für alle Ressourcenverwaltung üblich, explizit inklusive Speicher. Man braucht kein spezielles Interface und keine spezielle Aufrufssyntax, wo ein Destuktor ist, wird dieser am Ende des Blocks ausgeführt. Das C++-Äquivalent des Java-Beispiels oben wäre
{ std::ifstream file(filename); // ... } // file wird automatisch geschlossen (file.~ifstream() aufgerufen)Und mit Speicher macht man das halt auf die gleiche Weise in verwaltenden Klassen:
{ std::vector<int> v(100); // ... } // v wird hier zerstört, der Speicher sofort (!) freigegebenoder auch
{ std::unique_ptr<some_class> ptr(new some_class(foo, bar)); // ... } // ptr wird hier zerstört, sein Destruktor zerstört in der Folge das Objekt, auf das er zeigt.Soviel zur grundlegenden Idee. Jetzt für Fortgeschrittene: Es ist aus ein paar Gründen sinnvoll, die Speicherverwaltungsaufgaben möglichst eng zu kapseln, also für jede Ressource ein eigenes Verwaltungsobjekt zu haben. Der Grund dafür ist zum einen, dass es übersichtlicher ist, aber auch, dass man unnötige Kopfschmerzen in Eckfällen vermeidet.
Zum Beispiel sieht Folgendes für das ungeübte Auge völlig in Ordnung aus:
class some_class { public: some_class() : p(new some_other_class(1)), q(new some_other_class(2)) { } ~some_class() { delete p; delete q; } private: // Kopiersemantik abschalten. some_class(some_class const &); some_class &operator=(some_class const &); some_other_class *p, *q; };Aber was, wenn some_other_class jetzt beispielsweise so aussieht:
struct some_other_class { some_other_class(int n) { if(n == 2) throw n; } };Rückblick:
some_class() : p(new some_other_class(1)), q(new some_other_class(2)) // <-- das wirft jetzt eine Exception! { }Das some_class-Objekt ist nicht fertig konstruiert, der Destruktor kann also nicht bemüht werden; das ginge auch schief, weil q ins Nirvana zeigt. Zwar werden bereits konstruierte Teilobjekte in so einem Fall vor Verlassen des Konstruktors zerstört, aber nackte Zeiger haben keine Aufräumautomatik. Schon haben wir ein Speicherleck, und das ist gar nicht mal so einfach sinnvoll zu stopfen. Etwas der Form
some_class() try : p(new some_other_class(1)), q(new some_other_class(2)) { } catch(int n) { if(n == 2) delete p; }scheint möglich, aber will man sich das wirklich antun? Und wenn some_class mal weniger vorhersehbar Exceptions wirft, kriegt man noch ganz andere Probleme; so einfach ist nämlich nicht festzustellen, welche Zeiger schon gültige Werte haben.
Vergleiche dagegen das Szenario mit ressourcenverwaltenden Klassen:
class some_class { public: some_class() : p(new some_other_class(1)), q(new some_other_class(2)) { } private: // Kopiersemantik abschalten. some_class(some_class const &); some_class &operator=(some_class const &); std::unique_ptr<some_other_class> p; std::unique_ptr<some_other_class> q; };Hier kann some_other_class::some_other_class Exceptions werfen, wie es lustig ist, der Konstruktor zerstört ggf. die bereits konstruierten Teilobjekte wieder, und diese räumen den angeforderten Kram wieder weg. Merke auch: Ein eigener Destruktor ist hier gar nicht mehr notwendig, weil der generierte alle notwendigen Aufgaben übernimmt.
-
Das neue Resource-try ist doch nur noch eine weitere Verschiebung des Problems. Die sollen mal eine ordentliche, GC-gebundene Lösung bringen.