Garbage Collector
-
Also gezwungen nicht. Aber es wäre eben schön.
Die Simulationsblöcke sollen alle in einer Funktion
MY_MAIN(void)
erzeugt werden.
Damit sie nach dem Verlassen der Funktion noch existiern sollte das Dynamisch geschehen.
Jeder Simulationsblock lebt bis zum Ende der Simulation.
Und dann sollen sie aber zerstört werden.Ich glaube das Wort Garbage Collector ist etwas hochgegriffen gewesen von meiner Seite.
Der soll ja eigentlich prüfen ob noch ein Pointer auf die Instanz vorhanden ist. Und wennn nein, dann soll er das Objekt zerstören.
Das brauch ich garnicht.
Ich muss einfach alles was in der Funktion
MY_MAIN(void)
dynamisch erzeugt, und nicht mit delete zerstört worden ist dannach aufräume.
Wobei cih davon ausgehen darf, dass in dieser Funktion alle Objekte von
der Simulationsklass und damit von der Garbage Collect klasse erben.
-
Ein Problem dabei ist, dass C++ dem Programmierer ein bestimmtes Verhalten zusagt. Dazu gehört, dass ein mit new geholter Pointer bis zum delete gültig bleibt. Du greifst mit deinem Konzept in dieses System ein und setzt damit den gesamten C++-Standard außer Kraft. Wenn mal jemand dein System benutzt, der mehr von C++ versteht als du, der wird dich verfluchen.
Außerdem ist es eine üble Sache, Programmierfehler zu verstecken. Du bekämpfst damit Symptome schlampiger Programmierung, statt die Ursache zu beheben. Gib statt dessen eine fette Fehlermeldung aus, das macht es ein klein wenig besser (aber immer noch nicht gut).
Und dann die ganzen Fallen. Es gibt Geschichten wie placement new, die dir hier in die Suppe spucken könnten. Möglicherweise weißt du nicht einmal, was das überhaupt ist - das wäre dann ein sicheres Zeichen für dich, nicht in der Speicherverwaltung rumzupfuschen.
Und natürliche globale Objekte, die im Destruktor selbst ein delete machen, wobei dein Gehacke sofort übelst krachen geht, mit einem Programmabsturz, dessen Ursache kaum zu finden ist.
-
Also zunächst bleib ich dem verhalten von C++ treu, soweit es ein GUI programmierer kennt. Das passt eigentlich schon.
Der Programmierer Darf zerstören, muss aber nicht, so wie in den Meißten Bibliotheken.Ein Placement new würd ich hier jetzt nicht implementieren.
Das wäre eher interessante, wenn ich zeitkritisch was alloziieren möchte,
oder auch für objekte in Shared Memory könnt ich mir das vorstellen.
Placement new wird bei mir nicht vorkommen.Gruß
-
AlexanderKiebler schrieb:
Also zunächst bleib ich dem verhalten von C++ treu, soweit es ein GUI programmierer kennt. Das passt eigentlich schon.
...was soll man auf solche Ignoranz noch antworten? Du bleibst damit dem Verhalten von C++ ungefähr so treu, wie dein Geschreibsel der Deutschen Rechtschreibung treu bleibt. Also quasi überhaupt nicht.
Der Programmierer Darf zerstören, muss aber nicht, so wie in den Meißten Bibliotheken.
Doch, der Programmierer muss, weil er korrekt programmieren soll. Und wenn du ihm in die Quere kommst, dann darf er eben nicht mehr. Siehe globale Objekte.
Ein Placement new würd ich hier jetzt nicht implementieren.
Ach.
Ich lasse es mal gut sein hier in dem Thread. Vielleicht findet ein anderer bessere Argumente, die du verstehen kannst

-
AlexanderKiebler schrieb:
Ich hab mir das gerade mal durchgeguckt. Also die Smart Pointer von Boost.
Ich persöhnlich halte das ein bischen für "mit Kanonen auf Spatzen geschossen"Du musst bei einem GC ja auch darüber eine Liste führen, wenn der Zeiger noch verwendet wird, und dies geht nur mittels Mechanismen wie Referenzzählung. Zudem ist der shared_ptr, obwohl ein Schwergewicht (Den ich nur bei geteilten Ressourcen verwenden würde), auch in der Lage gegenseitige Objektverweise aufzulösen (Stichwort: weak_ptr). Mal sehen wie du das ohne den Aufwand nachbasteln willst.
GC und C++ vertragen sich wirklich nicht, zumindest nicht, wenn nicht Auflagen zur Verwendung oder noch besser der Compiler eine Lösung unter der Haube einbaut (Ist ja durchaus "optional" in Überlegung für einen späteren Technical Report).
-
Also ich habe mal ein Testprogramm geschrieben wo zeigt, wie cih das meine.
Es müßten noch die Operatoren new[] und delete[] überschreben werden, welche aber eine neue statische liste bekommen würden. Und die richtige Ausnahme schmeißen...
Hier die main.cpp#include <iostream> #include "TestClass.h" int main(void) { TestClass* a=new TestClass; a=new TestClass; a=new TestClass; a=new TestClass; delete a; a=new TestClass; a=new TestClass; a->remove_all_instances(); return 0; }Dann die klasse zum testen welche von GaCo abgeleitet wurde.
#ifndef TEST_CLASS_H #define TEST_CLASS_H #include "GaCo.h" class TestClass: public GaCo { public: TestClass(){std::cout <<"\n Construkt TestClass";}; }; #endifUnd zuletzt der GaCo =):
#ifndef GA_CO_H #define GA_CO_H #include <iostream> #include <set> class GaCo { public: /// Zerstört alle noch vorhandenen Objekte void remove_all_instances(void); ///Operatoren void * operator new (size_t s)throw(); void operator delete (void * ptr); private: static std::set<void *> instances; static std::set<void *>::iterator instances_it; }; #endifund
#ifndef GA_CO_CPP #define GA_CO_CPP #include "GaCo.h" #include <cstdlib> std::set<void *> GaCo::instances; std::set<void *>::iterator GaCo::instances_it; void GaCo::remove_all_instances(void) { instances_it=instances.begin(); while(instances_it!=instances.end()) { std::cout <<"\n ptr \t" <<*instances_it; free(*instances_it); instances_it++; } instances.clear(); std::cout <<"\n instances.size()\t" <<instances.size(); } void * GaCo::operator new(size_t s)throw() { std::cout <<"\n\noperator GaCo::new call"; void* ptr=std::malloc(s); if(ptr) instances.insert(ptr); else { ///Schmeißen } std::cout <<"\n instances.size()\t" <<instances.size(); std::cout <<"\n ptr \t" <<ptr; return ptr; } void GaCo::operator delete(void *ptr) { std::cout <<"\n\noperator GaCo::delete call"; instances_it=instances.find(ptr); if(instances_it!=instances.end()) instances.erase(instances_it); free(ptr); std::cout <<"\n instances.size()\t" <<instances.size(); std::cout <<"\n ptr \t" <<ptr; } #endifLäuft einwandfrei muss ich sagen.
Für Rechtschreibung, sorry, ich achte nicht gerade drauf.
-
AlexanderKiebler schrieb:
Also ich habe mal ein Testprogramm geschrieben wo zeigt, wie cih das meine.
Also unter einem GC verstehe zumindest ich eindeutig etwas anderes: Zum Beispiel löscht ein GC nur Objekte die nicht mehr verwendet werden. Davon abgesehen das ich Iteratoren nur lokal anlegen würde.
-
Ja ich mittlerweile auch. Wie bereits mehrmals gesagt, ist der Begriff Garbage Collector zu viel gesagt.
Dort müßt ich ja immer schauen ob noch ein Pointer auf mein objekt vorhanden ist, und dann löschen =).
habs schon öfters geschriebenhier, dass ich das nocht möchte.
Gruß
P.s.: Das wäre auch ne Menge Arbeit denke ich
-
AlexanderKiebler schrieb:
P.s.: Das wäre auch ne Menge Arbeit denke ich

Oder man verwendet shared_ptr/weak_ptr...

-
Bald soll ja neues C++ raus kommen

Freu mich schon riesig drauf. Meinst da ist ein Garbage Collector mit drin ??
Soll einiges von boost übernommen werden habe ich gehört.
-
AlexanderKiebler schrieb:
Bald soll ja neues C++ raus kommen

Freu mich schon riesig drauf. Meinst da ist ein Garbage Collector mit drin ??Nein, es ist kein GC enthalten. Es ist zwar ein optionaler GC langfristig angedacht (aber einer nach der Art: entweder für das ganze Programm, oder garnicht), aber nicht mehr für den Standard.
Wobei ich unter C++ keinen GC verwenden würde (unabhängig davon, das ich GCs nicht ablehne, nur passt es nicht zur C++ Programmierung).
-
AlexanderKiebler schrieb:
Was ist denn so schlecht daran einfach new und delete zu überladen ??
Ist das geächtet wie goto ??Nein. Das zu Überladen ist schon ok.
Aber das, was Du vor hast, kann nicht rundum funktionieren. Du wirst eine Frickellösung erhalten, die immer mit massig Ausnahmen une Einschränkungen leben muß. Große Kosten, aber praktisch kein Zugewinn. Zum Beispiel habe ich eine Klasse Button, die hat innendrin einen Zeiger auf das ihn besitzende Window. Im Button-Destruktor wird dem Window noch schnell bescheid gesagt. Vergißt der User, den Button zu löschen, ist das ein Fehler und der User muß bestraft werden.
Bei Dir hingegen wird der Button dann einfach nach der main gelsöcht, aber oh weh!, das Window ist ja schon lange weg und Du sagst totem Speicher bescheid. Das ist Abstürzen auf die umständliche Art.Mach doch statt dem Aufräumer einen Auflister! Der Listet alle Speicherbereiche auf, die zu löschen vergessen wurden! Der User muß dann halt sein Speicherloch suchen und dichtmachen.
-
AlexanderKiebler schrieb:
Läuft einwandfrei muss ich sagen.
Bei dir laufen gar keine Destruktoren. Du nimmst den Objekten einfach den Speicher weg, ohne sie zu "destruieren". Bei PODs kann man das so machen. Aber auch nur dann. Das hier klappt schon nicht mehr:
struct ich_bin_kein_pod { std::string name; int geburtsjahr; };und Du bekommst Speicherlecks ohne Ende.
Ich sehe -- wie jemand vorher auch schon erwähnt hat -- keine Notwendigkeit hier mit Überladung von new/delete "rumzufrickeln". Du leitest ja eh zu dem Zweck von einer Klasse ab. Und diese Klasse könnte ebenso gut im Konstruktor und Destruktor sich "irgendwo" anmelden und abmelden. Ein virtueller Destruktor ist da wahrscheinlich auch nicht verkehrt.
Hier eine Skizze:
#include <cassert> #include <utility> #include <type_traits> #include <boost/noncopyable.hpp> struct links : private boost::noncopyable { links* prev; links* next; links() : prev(0), next(0) {} void isolate() { if (prev) prev->next = next; if (next) next->prev = prev; prev = next = 0; } ~links() {isolate();} }; void insert_after(links & l1, links & l2) { assert(!l2.prev); assert(!l2.next); l2.next = l1.next; if (l1.next) l1.next->prev = &l2; l2.prev = &l1; l1.next = &l2; } class collectable : links { friend class collector; protected: collectable() {} public: virtual ~collectable() {} bool owned() const {return prev;} void break_free() {this->isolate();} }; class collector : links { public: template<class T, class...Args> typename std::enable_if<( std::is_convertible<T*,collectable*>::value), T* >::type cnew(Args&&...args) { T* p = new T(std::forward<Args>(args)...); collectable* q = p; insert_after(*this,*q); return p; } void take_over(collectable* t) { t->isolate(); insert_after(*this,*t); } ~collector(); }; collector::~collector() { while (next) { collectable* const tmp = static_cast<collectable*>(next); delete tmp; // <-- mutates next assert(next != tmp); } } #include <iostream> struct foo : collectable { int number; explicit foo(int i) : number(i) {} ~foo() {std::cout << "~foo, " << number << '\n';} }; int main() { collector c; foo* p = c.cnew<foo>(23); foo* q = c.cnew<foo>(1729); foo* z = new foo(42); c.take_over(z); delete p; // fine, Objekt is removed from c's list collector d; d.take_over(q); // steals *q from collector c } // <-- *q and *z will be destroyed hereDie Objekte, auf die p und q ind z zeigen "gehören" höchstens einem "Collector". Man kann sie manuell löschen oder von den Collectoren löschen lassen, wenn die Collectoren zerstört werden.
So etwas ähnliches machen die glaub'ich auch bei Qt -- also eine beidseitige Registrierung zwischen Besitzer und Besitz, so dass ein manuelles Löschen beim Besitzer auch automatisch gemeldet wird.
BTW: C++0x bringt von Haus aus keine Garbage-Collection mit.
kk
-
Ja das war auch mein erstes Mako was mit aufgefallen ist.
Denke ich kann den Destuktor aufrufen in dem ich
den GaCo destuktor virtuel mache, und bei der zerstörfunktion die entsprechenden deletes aufrufe. Bin aber gard noch dabei.Gruß
-
Kannst Dir ja mal mein Beispiel näher angucken. Das einzige, was ich anders mache und nicht deinem Plan entspricht ist, dass ich kein Singleton hier verwende sondern verschiedene "Collectoren" zulasse und ein Objekt, was ich per new erzeuge gehört auch nicht sofort einem Collector (welchem denn?). Das kannst Du natürlich anpassen und mit einem Singleton machen. Allerdings sehe ich den Sinn darin nicht ganz; denn damit würdest Du wirklich bis zum Programmende warten, um "aufzuräumen". Dann kannst Du es dir auch gleich fast sparen.
-
Also erstmal danke für deinen Quellcode.
Werd ihn mir mal genauer anschauen, aber das wird wieder etwas Zeit benötigen.
Kann mich mit boost noch nicht so gut aus, werd also die Klassen wo du von abgeleitet hast nachlesen müssen.
Schaut aber interessant aus was du dam machst.
Professioneller als mein Ansatz in jedem Fall =).
-
Ableiten von boost::noncopyable verhindert nur compiler-generierte Kopierkonstruktoren und Zuweisungsoperatoren. Sonst passiert da nichts.
Und sonst habe ich für das cnew template noch von C++0x Mitteln Gebrauch gemacht (Rvalue-Referenzen, Variadische Templates, Type Traits). Aber
foo* p = c.cnew<foo>(23);ist nur eine Abkürzung für
foo* p = new foo(23); c.take_over(p);kann man also auch weglassen.
-
AlexanderKiebler schrieb:
Also zunächst bleib ich dem verhalten von C++ treu, soweit es ein GUI programmierer kennt. Das passt eigentlich schon.
Der Programmierer Darf zerstören, muss aber nicht, so wie in den Meißten Bibliotheken.WTF???
Klar muss er, wer soll denn sonst, so lange keine Smart-Pointer im Spiel sind?
-
Na ja Also die QT baut ja einen Abhängigkeitsbaum auf.
Die mit new erzeugten Objekte werden dann automatisch vom Speicher genommen.
Man kann das Nachvollziehen, wenn man die destruktoren impelemntiert.So war das gemeint. Bei reinem C/C++ muss er natürlich....
Und bei der QT wird auch zerstört, aber eben automatisch.Habe aber noch ein anderes Problem.
Ich habe den Operator
void operator delete[] (void * ptr);überladen.
Leider wird beidelete[] *instances_arr_it;leider der operator
void operator delete (void * ptr);aufgerufen, was zu einem Speicherabsturz führt.
Woran liegt denn das ??
*instances_arr_it ist vom Typ const GaCo *Gruß
-
AlexanderKiebler schrieb:
Woran liegt denn das ??
lies mal ein anfängerbuch.