webentwicklung-frage-antwort-db.com.de

Smart Pointer: Oder wem gehört dein Baby?

In C++ dreht sich alles um den Besitz von Speicher
Aka "Besitzersemantik"

Es liegt in der Verantwortung des Besitzers eines Teils des dynamisch zugewiesenen Speichers, diesen Speicher freizugeben. So wird die Frage wirklich, wem die Erinnerung gehört.

In C++ wird der Besitz durch den Typ dokumentiert, in den ein RAW-Zeiger eingebunden ist. In einem guten (IMO) C++ - Programm kommt es daher sehr selten vor, dass RAW-Zeiger weitergegeben werden (da RAW-Zeiger keinen abgeleiteten Besitz haben, können wir dies nicht Sagen Sie, wem der Speicher gehört, und ohne sorgfältiges Lesen der Dokumentation können Sie nicht sagen, wer für das Eigentum verantwortlich ist.

Umgekehrt ist es selten, dass RAW-Zeiger in einer Klasse gespeichert werden. Jeder RAW-Zeiger wird in einem eigenen SMART Zeiger-Wrapper gespeichert. (NB .: Wenn Sie kein Objekt besitzen, sollten Sie es nicht aufbewahren, da Sie nicht wissen können, wann es außer Reichweite gerät und zerstört wird.)

Also die Frage:

  • Auf welche Art von Besitzersemantik sind Menschen gestoßen?
  • Welche Standardklassen werden verwendet, um diese Semantik zu implementieren?
  • Welche Situationen finden Sie nützlich?

Behalten wir eine Art von semantischem Besitz pro Antwort bei, damit sie einzeln hoch und runter abgestimmt werden können

Zusammenfassung:

Konzeptionell sind intelligente Zeiger einfach und naive Implementierungen einfach. Ich habe viele Implementierungsversuche gesehen, aber sie sind immer in einer Weise fehlerhaft, die für gelegentliche Anwendungen und Beispiele nicht offensichtlich ist. Daher empfehle ich, immer gut getestete "Smart Pointers" aus einer Bibliothek zu verwenden, anstatt Ihre eigenen zu rollen. std :: auto_ptr oder einer der Boost-Smart-Pointer decken scheinbar alle meine Bedürfnisse ab.

std :: auto_ptr <T>:

Einzelne Person besitzt das Objekt.
Eine Eigentumsübertragung ist jedoch zulässig.

Verwendung:
======
Hiermit können Sie Schnittstellen definieren, die die explizite Übertragung des Eigentums anzeigen.

boost :: scoped_ptr <T>

Einzelne Person besitzt das Objekt.
Eine Eigentumsübertragung ist NICHT gestattet.

Verwendung:
======
Wird verwendet, um expliziten Besitz anzuzeigen.
Objekt wird vom Destruktor oder beim expliziten Zurücksetzen zerstört.

boost :: shared_ptr <T> (std :: tr1 :: shared_ptr <T>)

Mehrfache Inhaberschaft.
Dies ist ein einfacher Zeiger mit Referenzzählung. Wenn der Referenzzähler Null erreicht, wird das Objekt zerstört.

Verwendung:
======
Wenn ein Objekt mehrere Ähren mit einer Lebensdauer haben kann, die zur Kompilierungszeit nicht bestimmt werden kann.

boost :: weak_ptr <T>

Wird mit shared_ptr <T> verwendet.
In Situationen, in denen ein Zyklus von Zeigern auftreten kann.

Verwendung:
======
Wird verwendet, um zu verhindern, dass Zyklen Objekte behalten, wenn nur der Zyklus eine gemeinsame Nachzählung führt.

113
Martin York

Für mich decken diese drei Arten die meisten meiner Bedürfnisse ab:

shared_ptr - Referenzzählung, Freigabe, wenn der Zähler Null erreicht

weak_ptr - wie oben, aber es ist ein "Sklave" für ein shared_ptr, kann die Zuordnung nicht aufheben

auto_ptr - wenn die Erstellung und Freigabe innerhalb derselben Funktion erfolgen oder wenn das Objekt immer nur als ein Eigentümer betrachtet werden muss. Wenn Sie einen Zeiger einem anderen zuweisen, "stiehlt" der zweite das Objekt vom ersten.

Ich habe meine eigene Implementierung für diese, aber sie sind auch in Boost verfügbar.

Ich übergebe weiterhin Objekte als Referenz (const, wann immer dies möglich ist). In diesem Fall muss die aufgerufene Methode davon ausgehen, dass das Objekt nur zum Zeitpunkt des Aufrufs aktiv ist.

Es gibt eine andere Art von Zeiger, die ich benutze und die ich hub_ptr nenne. Dies ist der Fall, wenn Sie ein Objekt haben, auf das von darin verschachtelten Objekten aus zugegriffen werden muss (normalerweise als virtuelle Basisklasse). Dies könnte gelöst werden, indem ein weak_ptr zu ihnen, aber es hat keine shared_ptr zu sich selbst. Da bekannt ist, dass diese Objekte nicht länger leben als er, wird ihnen ein hub_ptr übergeben (dies ist nur ein Template-Wrapper für einen regulären Zeiger).

20
Fabio Ceconello

Einfaches C++ Modell

In den meisten Modulen, die ich gesehen habe, wurde standardmäßig davon ausgegangen, dass das Empfangen von Zeigern nicht ist, das den Besitz erhält. In der Tat waren Funktionen/Methoden, die den Besitz eines Zeigers aufgeben, sehr selten und drückten diese Tatsache ausdrücklich in ihrer Dokumentation aus.

Bei diesem Modell wird davon ausgegangen, dass der Benutzer nur Eigentümer dessen ist, was er explizit zuweist.. Alles andere wird automatisch entsorgt (am Ende des Anwendungsbereichs oder über RAII). Dies ist ein C-ähnliches Modell, erweitert durch die Tatsache, dass die meisten Zeiger Objekte besitzen, die sie automatisch oder bei Bedarf freigeben (meistens bei der Zerstörung der Objekte), und dass die Lebensdauer der Objekte vorhersehbar ist (RAII ist dein Freund). nochmal).

In diesem Modell sind rohe Zeiger frei zirkulierend und meistens nicht gefährlich (aber wenn der Entwickler klug genug ist, verwendet er stattdessen Referenzen, wann immer dies möglich ist).

  • rohe Zeiger
  • std :: auto_ptr
  • boost :: scoped_ptr

Smart Pointed C++ - Modell

In einem Code voller intelligenter Zeiger kann der Benutzer hoffen, die Lebensdauer von Objekten zu ignorieren. Der Besitzer ist niemals der Benutzercode: Es ist der Smart Pointer selbst (wieder RAII). Das Problem ist, dass Zirkelverweise, die mit intelligenten Zeigern mit Referenzzählung gemischt werden, tödlich sein können, Sie müssen also sowohl mit gemeinsamen Zeigern als auch mit schwachen Zeigern umgehen. Sie müssen also noch die Eigentumsrechte berücksichtigen (der schwache Zeiger könnte durchaus auf nichts hindeuten, selbst wenn sein Vorteil gegenüber dem rohen Zeiger darin besteht, dass er Ihnen dies mitteilen kann).

  • boost :: shared_ptr
  • boost :: weak_ptr

Fazit

Egal welche Modelle ich beschreibe, es sei denn, der Empfang eines Zeigers ist eine Ausnahme, erhält nicht seinen Besitz und ist es) immer noch sehr wichtig zu wissen, wem gehört. Sogar für C++ - Code werden häufig Verweise und/oder intelligente Zeiger verwendet.

23
paercebal

Ich habe keinen gemeinsamen Besitz. Wenn Sie dies tun, stellen Sie sicher, dass es sich nur um Code handelt, den Sie nicht kontrollieren.

Das löst 100% der Probleme, da Sie verstehen müssen, wie alles zusammenwirkt.

10
MSN
  • Geteilter Besitz
  • boost :: shared_ptr

Wenn eine Ressource von mehreren Objekten gemeinsam genutzt wird. Der Boost shared_ptr verwendet die Referenzzählung, um sicherzustellen, dass die Ressource freigegeben wird, wenn alle erledigt sind.

2
Martin York

std::tr1::shared_ptr<Blah> ist nicht selten die beste Wahl.

2
Matt Cruikshank

Von Boost gibt es auch die Zeiger Container Bibliothek. Diese sind effizienter und einfacher zu verwenden als ein Standardcontainer mit intelligenten Zeigern, wenn Sie die Objekte nur im Kontext ihres Containers verwenden.

Unter Windows gibt es die COM-Zeiger (IUnknown, IDispatch und friends) und verschiedene Smart-Zeiger für deren Behandlung (z. B. die ATLs CComPtr und die Smart-Zeiger, die von der Anweisung "import" in automatisch generiert werden Visual Studio basierend auf der _ com_ptr Klasse).

2
Ryan Ginstrom
  • Ein Besitzer
  • boost :: scoped_ptr

Wenn Sie Speicher dynamisch zuweisen müssen, aber sicherstellen möchten, dass die Zuordnung an jedem Exit-Punkt des Blocks aufgehoben wird.

Ich finde das nützlich, da es leicht wieder eingesetzt und wieder freigegeben werden kann, ohne jemals über ein Leck nachdenken zu müssen

1
Pieter

Ich glaube nicht, dass ich jemals in der Lage war, mein Design mitzugestalten. Tatsächlich ist das Fliegengewichtmuster der einzig gültige Fall, an den ich denken kann.

1

yasper :: ptr ist eine leichte, boost :: shared_ptr-ähnliche Alternative. Es funktioniert gut in meinem (vorerst) kleinen Projekt.

Auf der Webseite unter http://yasper.sourceforge.net/ wird Folgendes beschrieben:

Warum einen anderen C++ - Smart Pointer schreiben? Es gibt bereits mehrere hochwertige Smart-Pointer-Implementierungen für C++, insbesondere das Boost-Pointer-Pantheon und Lokis SmartPtr. Für einen guten Vergleich der Smart Pointer-Implementierungen und wenn ihre Verwendung angemessen ist, lesen Sie bitte Herb Sutters The New C++: Smart (er) Pointer. Im Gegensatz zu den umfangreichen Funktionen anderer Bibliotheken ist Yasper ein eng fokussierter Referenzzählzeiger. Es stimmt eng mit den Richtlinien shared_ptr und RefCounted/AllowConversion von Loki überein. Mit Yasper können C++ - Programmierer die Speicherverwaltung vergessen, ohne die großen Abhängigkeiten von Boost einführen oder sich mit den komplizierten Richtlinienvorlagen von Loki vertraut machen zu müssen. Philosophie

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

Der letzte Punkt kann gefährlich sein, da yasper riskante (aber nützliche) Aktionen zulässt (z. B. Zuweisung zu unformatierten Zeigern und manuelle Freigabe), die von anderen Implementierungen nicht zugelassen werden. Seien Sie vorsichtig, verwenden Sie diese Funktionen nur, wenn Sie wissen, was Sie tun!

1
Hernán

Es gibt eine andere häufig verwendete Form des übertragbaren Eigentümers, und es ist vorzuziehen, auto_ptr weil es die Probleme vermeidet, die durch auto_ptr 's verrückte Korruption der Zuweisungssemantik.

Ich spreche von niemand anderem als swap. Jeder Typ mit einer geeigneten swap - Funktion kann als intelligente Referenz auf einen Inhalt verstanden werden, der ihm bis zur Übertragung des Eigentums an eine andere Instanz desselben Typs von gehört tauschen sie. Jede Instanz behält ihre Identität, wird jedoch an neue Inhalte gebunden. Es ist wie eine sicher rückbindbare Referenz.

(Es ist eher eine intelligente Referenz als ein intelligenter Zeiger, da Sie ihn nicht explizit dereferenzieren müssen, um auf den Inhalt zuzugreifen.)

Dies bedeutet, dass auto_ptr weniger notwendig wird - es wird nur benötigt, um die Lücken zu füllen, in denen Typen keine gute swap -Funktion haben. Aber alle Standardcontainer tun es.

1
  • Ein Besitzer: Aka löschen auf Kopie
  • std :: auto_ptr

Wenn der Ersteller des Objekts das Eigentum ausdrücklich an einen anderen übergeben möchte. Dies ist auch eine Möglichkeit, den Code zu dokumentieren, den ich Ihnen gebe, und ich verfolge ihn nicht mehr. Stellen Sie daher sicher, dass Sie ihn löschen, wenn Sie fertig sind.

0
Martin York