Eigenes Objekt löschen
-
Nexus schrieb:
FreakY<3Cpp schrieb:
Was würdet ihr von folgender Destroy-Methode halten und sagen ? [...]
Ich benutze sowas nicht, aber ich möchte eure Meinung dazu wissen.Ich finde sowas sehr schlecht, da unterschiedliche Kontrollebenen für Speicheranforderung und -freigabe erforderlich sind.
Wenn man in C++ ein
neweinsetzt, erwartet man normalerweise, dass man ein Objekt mitdeletewieder freigeben kann (von speziellen Anwendungsfällen wie Smart Pointers oder Pointer-Container mal abgesehen, bei denen man aber weiss, dass man sich nicht um die Freigabe zu kümmern hat). Ich finde auch C++-Bibliotheken sehr schlimm, die auf diese Weise versuchen, einen Garbage Collector nachzubauen und "benutzerfreundlicher" zu sein. Meistens resultiert das nur in Inkonsistenzen, unnötigem Overhead und Einschränkungen für den Benutzer.Zu den weiteren Sicherheits- und Fehleranfälligkeitsaspekten wurde ja bereits genügend gesagt...
Und wenn man ein
MyObject* MyObject::create()einsetzt kommt einMyObject->destroy()gar nicht so unerwartet und erfordert eben keine unterschiedlichen Kontrollebenen.Und natürlich sind da viele Spezialfälle, die solche Techniken erforderlich machen, aber das ist ja das schöne an C++: dass es eben auch für Spezialfälle geeignete Sprachmittel gibt und nicht nur für Standardfälle.
-
tntnet schrieb:
Und wenn man ein
MyObject* MyObject::create()einsetzt kommt einMyObject->destroy()gar nicht so unerwartet und erfordert eben keine unterschiedlichen Kontrollebenen.Eine statische Factory-Methode? Also sowas?
MyClass* ptr = MyClass::create();Aber für die Zerstörung gibt es keine analoge Methode, sondern das muss das Objekt selber übernehmen?
ptr->Destroy();Das sieht mir nicht gerade nach gleicher Kontrollebene aus, geschweige denn würde ich sowas als "erwartet" bezeichnen.
tntnet schrieb:
Und natürlich sind da viele Spezialfälle, die solche Techniken erforderlich machen, aber das ist ja das schöne an C++: dass es eben auch für Spezialfälle geeignete Sprachmittel gibt und nicht nur für Standardfälle.
Meiner Ansicht resultieren aber viele dieser Spezialfälle aus fehlerhaftem Design. In den meisten Fällen sollte es eigentlich möglich sein, ein Objekt konsistent zu erzeugen und zu zerstören. Zumindest hatte ich selber noch nie einen Fall, bei dem ich auf ein übles
delete thiszurückgreifen musste, und kann mir das deshalb vielleicht auch zu wenig vorstellen.
-
Ich fand sowas ganz hilfreich im Umfeld von Multithreading-Anwendungen... der Worker-Thread ist in einem Objekt gekapselt, und sobald er seinen Job erledigt hat löscht er sich höflicherweise selbst einschließlich seines Datenobjekts.
Ansonsten muß man immer noch einen "Anker" haben, der ruhende Threads entfernt. Das schafft nur überflüssige Synchronisationsaufwände.
-
@Marc++us,
Wäre bei sowas nicht ein Threadpool besser geeignet? Sobald ein Workerthread seine Arbeit beendet hat, wird er nicht zerstört, sondern legt sich schlafen, bis der Pool, zu welchem der Thread gehört, ihm eine neue Aufgabe zuteilt und wieder weckt.Für das erstellen und löschen von Threads ist dann die Klasse zuständig, welche den Pool verwaltet.
Grüssli
-
Dravere schrieb:
@Marc++us,
Wäre bei sowas nicht ein Threadpool besser geeignet? Sobald ein Workerthread seine Arbeit beendet hat, wird er nicht zerstört, sondern legt sich schlafen, bis der Pool, zu welchem der Thread gehört, ihm eine neue Aufgabe zuteilt und wieder weckt.
Für das erstellen und löschen von Threads ist dann die Klasse zuständig, welche den Pool verwaltet.
Grüsslidas ändert nix. dann ist es eben das Job-Objekt, das sich nach erledigung des jobs selber löscht (und dabei den zwischenzeitlich an sich gebundenen thread aus dem pool freigibt).
-
volkard schrieb:
das ändert nix. dann ist es eben das Job-Objekt, das sich nach erledigung des jobs selber löscht (und dabei den zwischenzeitlich an sich gebundenen thread aus dem pool freigibt).
So war das nicht gemeint. Zum einen soll der Job nichts weiteres sein als eine Art von Funktionszeiger, bzw. zum Beispiel ein
boost::function<void()>, zum anderen soll der Thread nicht gelöscht werden, sondern eben schlafen gelegt. Das Objekt bleibt somit bestehen, der Thread wird nur über ein Event oder Mutex schlafen gelegt.Kurze, schnelle und womglich hässliche Implementierung, nur zur Verdeutlichung der Idee:
#include <boost/thread.hpp> #include <boost/scoped_ptr.hpp> #include <boost/noncopyable.hpp> #include <boost/ptr_container/ptr_vector.hpp> #include <vector> #include <cassert> #include <algorithm> #include <functional> class WorkerThread : public boost::noncopyable { // Attributes // private: bool m_continue; boost::function<void()> m_job; boost::mutex m_condition; boost::scoped_ptr<boost::thread> m_thread; // Constructor // public: WorkerThread() : m_continue(true) , m_job() , m_condition() , m_thread() { m_condition.lock(); m_thread.reset(new boost::thread(&WorkerThread::run, this)); } WorkerThread(boost::function<void()> job) : m_continue(true) , m_job(job) , m_condition() , m_thread() { m_condition.lock(); m_thread.reset(new boost::thread(&WorkerThread::run, this)); } // Methods // private: bool wait_on_job() { while(m_continue && !m_job) { m_condition.lock(); } return m_continue; } void run() { boost::mutex mutex; boost::unique_lock<boost::mutex> lock(mutex); while(wait_on_job()) { m_job(); m_job.clear(); } } public: bool is_free() const { return m_job.empty(); } void stop() { m_continue = false; m_condition.unlock(); } void set_job(boost::function<void()> job) { assert(!job.empty()); m_job = job; m_condition.unlock(); } void join() { m_thread->join(); } }; class WorkerThreadPool : public boost::noncopyable { // Typedefs // private: typedef boost::ptr_vector<WorkerThread> Pool; // Attributes // private: Pool m_pool; // Constructor // public: WorkerThreadPool() : m_pool() { } ~WorkerThreadPool() { std::for_each( m_pool.begin(), m_pool.end(), std::mem_fun_ref(&WorkerThread::join)); } // Methods // private: WorkerThread& get_worker() { Pool::iterator iter = m_pool.begin(); Pool::iterator end = m_pool.end(); for(; iter != end; ++iter) { if(iter->is_free()) { return *iter; } } m_pool.push_back(new WorkerThread()); return m_pool.back(); } public: void execute(boost::function<void()> job) { WorkerThread& worker = get_worker(); worker.set_job(job); } };So ist die Speicherverwaltung geregelt und es müssen nicht immer neue Threads erstellt werden, dadurch werden auch kleinere Aufgaben interessant, um sie einem Thread zu übergeben.
Grüssli
-
Simon2 schrieb:
hmmmm? schrieb:
Ist das eigentlich eine spezialität von C++ Programmieren, dass sie alles mögliche was man an komischem Code schreiben kann, auch machen
Ich glaube, es ist eine Spezialität von C++-Programmierern, dass sie sich gerne mit den Möglichkeiten und Grenzen der Sprache machen und gerne Neues ausprobieren.
Nur gibts meistens viel einfachere Lösungen für das eigentliche Problem. Liegt aber glaub ich auch an diesem Forum hier, da werden sehr oft Fragen zu den komischsten Implementierungsideen beantwortet, anstatt mal nach dem eigentlichen Problem zu fragen.
-
hmmmm? schrieb:
...Nur gibts meistens viel einfachere Lösungen für das eigentliche Problem. Liegt aber glaub ich auch an diesem Forum hier, da werden sehr oft Fragen zu den komischsten Implementierungsideen beantwortet, anstatt mal nach dem eigentlichen Problem zu fragen.

Also dann hast Du diesen Thread (und viele andere, die ich kenne) nicht richtig gelesen.
Der quillt doch über von "Gibt garantiert bessere Lösungen für ein konkretes Problem" (allein ich habe mich ja schon fast müde geschrieben daran).
... und das gilt für das Forum insgesamt: Eigentlich ist IMMER die erste Gegenfrage "Welches Problem willst Du eigentlich lösen?"
Dein Einwand klingt eher nach Bashing ...Aaaber: Wie leicht aus dem Ausgangspost herauszulesen ist, geht es hier nicht um die Lösung eines bestimmten Problems, sondern um die Chancen und Risiken eines bestimmten (durch die Sprache erlaubten) Konstrukts.
Das ist wie die Frage "Wie funktioniert eigentlich eine Einspritzpumpe am Auto?"
Man muss ja keine Lust haben, sich mit dieser Frage zu beschäftigen, aber einfach zu sagen: "Wo willst Du denn hin?" - beantwortet weder die Frage, noch hilft sie sonst weiter.
Natürlich kann man sagen: "Brauchst Du nicht zu wissen!" (was vielleicht sogar stimmt) ... aber das hat Neugierde zum Glück noch nie aufhalten oder abwerten können.Gruß,
Simon2.
-
Ging jetzt auch nicht speziell um diesen Thread. Sicher gibts einige die erst mal nach dem eigentlichen Problem fragen, aber die die komplizierte Lösungen posten sind meisten mehr.
-
hmmmm? schrieb:
Ging jetzt auch nicht speziell um diesen Thread. Sicher gibts einige die erst mal nach dem eigentlichen Problem fragen, aber die die komplizierte Lösungen posten sind meisten mehr.
Es gibt halt viele Wege etwas zu lösen und das sieht man dann auch hier. Sei doch froh, dass sich viele hier die Mühe machen lange über ein Problem, Lösungen und dessen Folgen zu machen. Oftmals hilft das möglicherweise wirklich nicht direkt zu dem eigentlichem Problem, aber man kann andersweitig davon profitieren.
-
Ob man nun "delete this;" schreibt, oder "delete obj;" in einer Free-Function, ist doch im Endeffekt egal.
Bei intrusive reference counting (wurde ja schon als Beispiel gebracht) setze ich persönlich z.B. grundsätzlich Memberfunktionen ein. Und das führt automatisch zu einem "delete this;".