Heap und Stack
-
eine DLL mit Funktionen zur E/A und Verarbeitung von asynchron eintreffenden Datenpaketen, die als Objekte verwaltet werden. Zu jedem Zeitpunkt ist null oder ein Datenpaket-Objekt ausgewählt und dazu an einen globalen Zeiger gebunden; in letzterem Fall beziehen sich die folgenden Funktionsaufrufe auf das ausgewählte Objekt. Zeitpunkt und Reihenfolge des Eintreffens der Datenpakete ist ebensowenig vorhersehbar wie die Festlegung des aktuell ausgewählten Pakets; Zeitpunkt, Art und Argumente der Funktionsaufrufe zur E/A und Verarbeitung der Datenpakete.
weitere Anwendungen:
Interfacing mit C;
Objekte mit genau bekannter Lebensdauer und mehreren Benutzern;
legacy code;Beispiele und Übungsaufgaben aus Textbüchern lassen sich nicht immer 1:1 auf die Praxis bei gegebenen Randbedingungen in Industrie und Lehre übertragen.
-
großbuchstaben schrieb:
eine DLL mit Funktionen zur E/A und Verarbeitung von asynchron eintreffenden Datenpaketen, die als Objekte verwaltet werden. Zu jedem Zeitpunkt ist null oder ein Datenpaket-Objekt ausgewählt und dazu an einen globalen Zeiger gebunden; in letzterem Fall beziehen sich die folgenden Funktionsaufrufe auf das ausgewählte Objekt. Zeitpunkt und Reihenfolge des Eintreffens der Datenpakete ist ebensowenig vorhersehbar wie die Festlegung des aktuell ausgewählten Pakets; Zeitpunkt, Art und Argumente der Funktionsaufrufe zur E/A und Verarbeitung der Datenpakete.
Ich muss zugeben, dass ich mit deinem Beispiel nichts anzufangen weiss. Wichtig ist für mich: Wem gehört das Objekt? (sprich: Wer gibt es wieder frei?). Nackte Zeiger an sich sind völlig in Ordnung, die verwende ich auch massenhaft. Allerdings ist keiner davon ein "besitzender" Zeiger, der irgendwann ein
deleteoderfreeoder ähnliches benötigt. D.h. es existiert irgendwo im Programm einunique_ptr/shared_ptroder eine Member/Stack-Variable von dem der Pointer via addressof-Operator geholt wurde, die der eigentliche Besitzer der Objektinstanz ist, und sicherstellt, dass der Pointer mindestens so lange gültig ist, wie darauf zugegriffen wird.Kommt der Pointer aus der DLL, dann ist es entweder nicht meine Aufgabe, das Objekt wieder freizugeben, oder aber ich muss eine
destroyObject()-Funktion oder ähnliches aufrufen. Im letzteren Fall packe ich den Pointer üblicherweise in einen Smart Pointer mit speziellem Deleter, derdestroyObject()automatisch aufruft.großbuchstaben schrieb:
weitere Anwendungen:
Interfacing mit C;
Lustigerweise arbeite ich gerade an einem Projekt, bei dem ich intensiv mit C-Bibliotheken zu tun habe, und ich muss sagen, dass ich dabei bisher sehr gute Erfahrungen mit
unique_ptr/shared_ptroder aber auchMicrosoft::WRL:ComPtr(Teile des Projekts verwenden unter Windows DirectX) gemacht habe.Eine der verwendeten Bibliotheken nutzt z.B. intensiv GLib/GObject, bei denen man oft Objekte in die Hand bekommt, bei denen man sich selbst darum kümmern muss, deren Referenzzähler zu dekrementieren, wenn man damit fertig ist. Ich muss sagen, dass gerade der
unique_ptrin diesen Fällen eine ziemliche Erleichterung ist:struct GObjectDeleter { inline void operator()(gpointer object) { assert(G_IS_OBJECT(object)); g_object_unref(object); } }; std::unique_ptr<IrgendeineGObjectKlasse, GObjectDeleter> object(gib_mir_objekt_mit_inkrementiertem_referenzzaehler());... das erspart eine ganze Menge redundante
g_object_unref()mit denen man sonst alle nur erdenklichen Code-Pfade zupflastern muss, wenn man nicht auf unübersichtlichegoto-Anweisungen zurückgreifen will.großbuchstaben schrieb:
Objekte mit genau bekannter Lebensdauer und mehreren Benutzern;
Wie eingangs erwähnt, irgendwer ist der Besitzer über den sich die "bekannte Lebensdauer" definiert. Ich habe z.B. auch Objkete in meinen Projekten die immer einem Elternobjekt zugeordnet sind, und höchstens so lange leben wie das Elternobjekt. In diesem Fall sehe ich ein Problem darin, wenn die Kinder einen nackten Eltern-Pointer haben - schließlich ist das kein "besitzender" Zeiger.
großbuchstaben schrieb:
legacy code;
Ja, da kommt man vielleicht manchmal nicht drumherum, es schadet aber nicht für neue Code-Teile trotzdem auf die Smart-Pointer zurückzugreifen und an den Schnittstellen ähnlich zu verfahren wie bei C-Bibliotheken (s.o.)
Finnegan
-
Finnegan schrieb:
Wichtig ist für mich: Wem gehört das Objekt? (sprich: Wer gibt es wieder frei?).
in der von mir beschriebenen Anwendung: dem Benutzer, der über die DLL externs die Funktionen abruft, mit denen die Daten verarbeitet werden. Da nur der Benutzer (bzw sein top-level Programm) weiß, wann er alle DLL Funktionen zur Verarbeitung aufgerufen hat, kann auch nur er bestimmen, wann das Datenpaket-Objekt gelöscht werden kann.
Ein smart pointer müßte zu diesem Zweck schon ziemlich smart sein, d.h. die Intention des Benutzers bzw top-level Programms erkennen, um zu wissen, wann das Datenpaket gelöscht werden kann, weil keine zukünftigen Funktionsabrufe mehr kommen, die sich auf das betreffende Objekt beziehen.
-
großbuchstaben schrieb:
in der von mir beschriebenen Anwendung: dem Benutzer, der über die DLL externs die Funktionen abruft, mit denen die Daten verarbeitet werden. Da nur der Benutzer (bzw sein top-level Programm) weiß, wann er alle DLL Funktionen zur Verarbeitung aufgerufen hat, kann auch nur er bestimmen, wann das Datenpaket-Objekt gelöscht werden kann.
Und warum nun new und rohe Pointer? Der Benutzer ist hier doch eindeutig der Besitzer.
Interfacing mit C;
Erzähl mal, wieso deine Schnittstelle sich für die Interna interessiert.
Objekte mit genau bekannter Lebensdauer und mehreren Benutzern;
Also ein automatisches Objekt?
legacy code;
Aha. Es ist also ein guter Grund, new und rohe Pointer benutzen, weil andere Leute auf der Welt es falsch gemacht haben?
-
großbuchstaben schrieb:
in der von mir beschriebenen Anwendung: dem Benutzer, der über die DLL externs die Funktionen abruft, mit denen die Daten verarbeitet werden. Da nur der Benutzer (bzw sein top-level Programm) weiß, wann er alle DLL Funktionen zur Verarbeitung aufgerufen hat, kann auch nur er bestimmen, wann das Datenpaket-Objekt gelöscht werden kann.
Es ist ja nicht so, dass Smartpointer die Objekte völlig willkürlich löschen. Man hat ja schon die Kontrolle darüber - man muss lediglich dafür sorgen, dass das Smartpointer-Objekt so lange existiert, wie das Datenpaket benötigt wird.
großbuchstaben schrieb:
Ein smart pointer müßte zu diesem Zweck schon ziemlich smart sein, d.h. die Intention des Benutzers bzw top-level Programms erkennen, um zu wissen, wann das Datenpaket gelöscht werden kann, weil keine zukünftigen Funktionsabrufe mehr kommen, die sich auf das betreffende Objekt beziehen.
Ich habe das mit den Datenpaketen zwar immer noch nicht so ganz verstanden, aber irgendwie riecht das ein wenig nach einem Fall für
std::shared_ptr.
Wenn man nicht weiss wann und für und wie lange etwas "zukünftig" gebraucht wird, bekommt halt jeder der (asynchron) etwas damit machen will einen Shared-Pointer auf das Datenpaket mit auf den Weg, und löscht diesen, wenn er damit fertig ist. Dasdeletepassiert dann automatisch wenn alle mit dem Ding durch sind. Gerade in Multithreaded-Code iststd::shared_ptroft ein wahrer Segen
und nebenbei: So "smart" braucht so ein Shared Pointer dabei gar nicht zu sein. Wenn es im Programmcode keine Referenz mehr auf ein Objekt gibt (und man sich nicht hinterlistigerweise einen nicht-Shared-Pointer darauf herausgewieselt hat), dann kann niemand mehr darauf zugreifen oder einen Funktionsaufruf damit machen, weil niemand mehr weiss wo sich das Objekt im Speicher befindet - ergo kann man es auch ruhigen Gewissens löschen.
Finnegan
-
SeppJ schrieb:
Und warum nun new und rohe Pointer? Der Benutzer ist hier doch eindeutig der Besitzer.
der Benutzer sitzt aber nicht in der DLL, sondern ruft externs auf. Asynchron. Mannmannmannnmannmann ...
Objekte mit genau bekannter Lebensdauer und mehreren Benutzern;
Also ein automatisches Objekt?
kommt drauf an. Wenn die Erzeugung von Objekten asynchron von einer darüber liegenden Schicht veranlaßt wird, eher nicht.
Aha. Es ist also ein guter Grund, new und rohe Pointer benutzen, weil andere Leute auf der Welt es falsch gemacht haben?
hab' keine Lust, mich zu wiederholen

-
großbuchstaben schrieb:
Finnegan schrieb:
Wichtig ist für mich: Wem gehört das Objekt? (sprich: Wer gibt es wieder frei?).
in der von mir beschriebenen Anwendung: dem Benutzer, der über die DLL externs die Funktionen abruft, mit denen die Daten verarbeitet werden. Da nur der Benutzer (bzw sein top-level Programm) weiß, wann er alle DLL Funktionen zur Verarbeitung aufgerufen hat, kann auch nur er bestimmen, wann das Datenpaket-Objekt gelöscht werden kann.
Ein smart pointer müßte zu diesem Zweck schon ziemlich smart sein, d.h. die Intention des Benutzers bzw top-level Programms erkennen, um zu wissen, wann das Datenpaket gelöscht werden kann, weil keine zukünftigen Funktionsabrufe mehr kommen, die sich auf das betreffende Objekt beziehen.
Falls die Zerstörung des Objektes vom Client-Programm initiiert werden muss, dann ist der Client der Besitzer, und die Frage nach dem Smartpointer stellt sich auch nur für das Clientprogramm.
-
großbuchstaben schrieb:
SeppJ schrieb:
Und warum nun new und rohe Pointer? Der Benutzer ist hier doch eindeutig der Besitzer.
der Benutzer sitzt aber nicht in der DLL, sondern ruft externs auf. Asynchron.
Ja, und?
Wenn die Erzeugung von Objekten asynchron von einer darüber liegenden Schicht veranlaßt wird, eher nicht.
Ja, und? Ist "asynchron" für dich irgendwie ein Zauberwort für undefinierte Besitzverhältnisse?
-
camper schrieb:
-
Finnegan schrieb:
Wenn man nicht weiss wann und für und wie lange etwas "zukünftig" gebraucht wird, bekommt halt jeder der (asynchron) etwas damit machen will einen Shared-Pointer auf das Datenpaket mit auf den Weg, und löscht diesen, wenn er damit fertig ist. Das
deletepassiert dann automatisch wenn alle mit dem Ding durch sind.eben nicht. Wann wer damit fertig ist, kann innerhalb der DLL nicht festgestellt werden, weil die Auswahl und Reihenfolge der Funktionen an einer höheren Schicht liegt, auf die die DLL keinen Zugriff hat.
Also nix mit automatisch löschen und sowas. Schön konservativ mit Zeiger und new.
Es bleibt bei dem, was ich in meinem ersten Post schrieb: raw pointers und new haben ihre Anwendungen, und damit ist die Diskussion für mich beendet.
-
großbuchstaben schrieb:
Finnegan schrieb:
Wenn man nicht weiss wann und für und wie lange etwas "zukünftig" gebraucht wird, bekommt halt jeder der (asynchron) etwas damit machen will einen Shared-Pointer auf das Datenpaket mit auf den Weg, und löscht diesen, wenn er damit fertig ist. Das
deletepassiert dann automatisch wenn alle mit dem Ding durch sind.eben nicht. Wann wer damit fertig ist, kann innerhalb der DLL nicht festgestellt werden, weil die Auswahl und Reihenfolge der Funktionen an einer höheren Schicht liegt, auf die die DLL keinen Zugriff hat.
Also nix mit automatisch löschen und sowas. Schön konservativ mit Zeiger und new.
Es bleibt bei dem, was ich in meinem ersten Post schrieb: raw pointers und new haben ihre Anwendungen, und damit ist die Diskussion für mich beendet.
Wenn du weißt, wann es sicher ist ein C++-
deletezu machen, kannst du auch genau so gut sicherstellen, dass das Smart-Pointer-Objekt bis zu eben dieser Stelle existiert, bzw. dort stattdessen einptr.reset()machen. Es ist ja nicht so, dass der Code mit Smart Pointern dann unbedingt "korrekter" oder "sicherer" wäre (mal abgesehen davon, dass man dann dasdeleteauf jeden Fall nicht "vergisst" und auch die Gefahr eines use-after-free minimiert).
Soweit ich mich erinnere, ging es doch eigentlich darum, dass man an dieser Stellenew/deleteunbedingt braucht ("Anwendungen" vonnewund besitzenden Raw Pointern), und so wie ich das sehe ist das hier nicht der Fall - zumindest habe ich noch kein Argument gelesen, weshalb man das Problem nicht mit einem Smart Pointer lösen könnte.Mir persönlich sind ehrlich gesagt Pointer lieber, die versehentlich (automatsich) freigegeben werden und danach genullt sind (da weiss man wo der Feind steht :D), als solche bei denen ich eventuell das
deletevergessen habe, oder schlimmer als solche die zwar gelöscht wurden, danach aber nicht genullt wurden
Und ja, ich habe auch fertig :p
Finnegan