In C ++ dreht sich alles um Speicherbesitz - auch bekannt als Besitzersemantik .
Es liegt in der Verantwortung des Besitzers eines Teils des dynamisch zugewiesenen Speichers, diesen Speicher freizugeben. Die Frage wird also wirklich, wem die Erinnerung gehört.
In C ++ Eigentum dokumentiert wird durch die Art eines roh ist Zeiger in so in einem guten (IMO) C ++ Programm ist es sehr selten (gewickelte selten , nicht nie ) um rohe Zeiger zu sehen weitergegeben (wie rohe Zeiger nicht so Eigentum geschlossen haben , können wir nicht sagen, wem der Speicher gehört, und daher können Sie ohne sorgfältiges Lesen der Dokumentation nicht sagen, wer für den Besitz verantwortlich ist).
Umgekehrt ist es selten, dass Rohzeiger in einer Klasse gespeichert werden. Jeder Rohzeiger wird in einem eigenen Smart-Pointer-Wrapper gespeichert. ( NB: Wenn Sie kein Objekt besitzen, sollten Sie es nicht speichern, da Sie nicht wissen können, wann es außerhalb des Gültigkeitsbereichs liegt und zerstört wird.)
Also die Frage:
- Auf welche Art von Besitzsemantik sind Menschen gestoßen?
- Welche Standardklassen werden verwendet, um diese Semantik zu implementieren?
- In welchen Situationen finden Sie sie nützlich?
Behalten wir 1 Art semantischen Eigentums pro Antwort bei, damit sie einzeln nach oben und unten abgestimmt werden können.
Zusammenfassung:
Konzeptionell sind intelligente Zeiger einfach und eine naive Implementierung einfach. Ich habe viele versuchte Implementierungen gesehen, aber sie sind immer auf eine Weise kaputt, die für gelegentliche Verwendung und Beispiele nicht offensichtlich ist. Daher empfehle ich, immer gut getestete Smart Pointer aus einer Bibliothek zu verwenden, anstatt Ihre eigenen zu rollen. std::auto_ptr
oder einer der intelligenten Boost-Zeiger scheint alle meine Bedürfnisse abzudecken.
std::auto_ptr<T>
::
Einzelne Person besitzt das Objekt. Eigentumsübergang ist zulässig.
Verwendung: Hiermit können Sie Schnittstellen definieren, die die explizite Eigentumsübertragung anzeigen.
boost::scoped_ptr<T>
Einzelne Person besitzt das Objekt. Eigentumsübergang ist NICHT erlaubt.
Verwendung: Wird verwendet, um explizites Eigentum anzuzeigen. Das Objekt wird vom Destruktor zerstört oder beim expliziten Zurücksetzen.
boost::shared_ptr<T>
( std::tr1::shared_ptr<T>
)
Mehrfachbesitz. Dies ist ein einfacher Zeiger mit Referenzzählung. Wenn der Referenzzähler Null erreicht, wird das Objekt zerstört.
Verwendung: Wenn ein Objekt mehrere Owers mit einer Lebensdauer haben kann, die zur Kompilierungszeit nicht bestimmt werden kann.
boost::weak_ptr<T>
::
Wird shared_ptr<T>
in Situationen verwendet, in denen ein Zeigerzyklus auftreten kann.
Verwendung: Wird verwendet, um zu verhindern, dass Zyklen Objekte behalten, wenn nur der Zyklus eine gemeinsame Nachzählung verwaltet.
quelle
In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO)
Kann das umformuliert werden? Ich verstehe es überhaupt nicht.In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around
. RAW-Zeiger haben keine Besitzersemantik. Wenn Sie den Eigentümer nicht kennen, wissen Sie nicht, wer für das Löschen des Objekts verantwortlich ist. Es gibt mehrere Standardklassen, die zum Umschließen von Zeigern (std :: shared_ptr, std :: unique_ptr usw.) verwendet werden, die den Eigentümer und damit definieren Definieren Sie, wer für das Löschen des Zeigers verantwortlich ist.Antworten:
Für mich decken diese 3 Arten die meisten meiner Bedürfnisse ab:
shared_ptr
- Referenzzählung, Freigabe, wenn der Zähler Null erreichtweak_ptr
- wie oben, aber es ist ein "Sklave" für einenshared_ptr
, kann nicht freigebenauto_ptr
- wenn die Erstellung und Freigabe innerhalb derselben Funktion erfolgt 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 verfügbar
Boost
.Ich übergebe Objekte
const
immer noch als Referenz ( wann immer möglich). In diesem Fall muss die aufgerufene Methode davon ausgehen, dass das Objekt nur während 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 über ein Objekt verfügen, auf das über darin verschachtelte Objekte zugegriffen werden muss (normalerweise als virtuelle Basisklasse). Dies könnte gelöst werden, indem ein a
weak_ptr
an sie weitergegeben wird, aber es hat kein ashared_ptr
für sich. Da diese Objekte nicht länger als er leben würden, wird ihnen ein hub_ptr übergeben (es ist nur ein Template-Wrapper an einen regulären Zeiger).quelle
noncopyable
und dass das Eigentum nicht übertragen werden kann.Einfaches C ++ - Modell
In den meisten Modulen, die ich gesehen habe, wurde standardmäßig angenommen, dass das Empfangen von Zeigern keinen Besitz erhält. Tatsächlich waren Funktionen / Methoden, die den Besitz eines Zeigers aufgaben, sehr selten und drückten diese Tatsache in ihrer Dokumentation ausdrücklich aus.
Bei diesem Modell wird davon ausgegangen, dass der Benutzer nur Eigentümer dessen ist, was er explizit zuweist . Alles andere wird automatisch entsorgt (beim Verlassen des Bereichs oder über RAII). Dies ist ein C-ähnliches Modell, das durch die Tatsache erweitert wird, dass die meisten Zeiger Eigentum von Objekten sind, die sie automatisch oder bei Bedarf freigeben (meistens bei der Zerstörung dieser Objekte), und dass die Lebensdauer von Objekten vorhersehbar ist (RAII ist Ihr Freund, nochmal).
In diesem Modell sind Rohzeiger frei zirkulierend und meist nicht gefährlich (aber wenn der Entwickler klug genug ist, verwendet er stattdessen Referenzen, wann immer dies möglich ist).
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 Zirkelreferenzen, die mit intelligenten Zeigern mit Referenzzählung gemischt werden, tödlich sein können. Sie müssen sich also sowohl mit gemeinsam genutzten als auch mit schwachen Zeigern befassen. Sie müssen also noch das Eigentum berücksichtigen (der schwache Zeiger könnte durchaus auf nichts verweisen, selbst wenn sein Vorteil gegenüber dem rohen Zeiger darin besteht, dass er es Ihnen sagen kann).
Fazit
Unabhängig von den Modellen, die ich beschreibe, erhält der Empfang eines Zeigers , außer in Ausnahmefällen, nicht sein Eigentum, und es ist immer noch sehr wichtig zu wissen, wem wer gehört . Selbst für C ++ - Code werden häufig Referenzen und / oder intelligente Zeiger verwendet.
quelle
Ich habe kein gemeinsames Eigentum. 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.
quelle
Wenn eine Ressource von mehreren Objekten gemeinsam genutzt wird. Der Boost shared_ptr verwendet die Referenzzählung, um sicherzustellen, dass die Zuweisung der Ressource aufgehoben wird, wenn alle fertig sind.
quelle
std::tr1::shared_ptr<Blah>
ist ziemlich oft die beste Wahl.quelle
Ab Boost gibt es auch die Zeigercontainerbibliothek . Diese sind etwas effizienter und benutzerfreundlicher 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 Freunde) und verschiedene intelligente Zeiger für deren Verarbeitung (z. B. CComPtr der ATL und die intelligenten Zeiger, die von der Anweisung "import" in Visual Studio basierend auf der Klasse _com_ptr automatisch generiert werden) ).
quelle
Wenn Sie Speicher dynamisch zuweisen müssen, aber sicher sein möchten, dass er an jedem Exit-Punkt des Blocks freigegeben wird.
Ich finde das nützlich, da es leicht wieder eingesetzt und freigegeben werden kann, ohne sich jemals um ein Leck sorgen zu müssen
quelle
Ich glaube nicht, dass ich jemals in der Lage war, an meinem Design mitzuwirken. Tatsächlich ist der einzige gültige Fall, den ich mir vorstellen kann, das Fliegengewichtsmuster.
quelle
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:
quelle
Es gibt eine andere häufig verwendete Form des einzelnen übertragbaren Eigentümers, und dies ist vorzuziehen,
auto_ptr
da dadurch die Probleme vermieden werden, die durchauto_ptr
die verrückte Beschädigung der Zuweisungssemantik verursacht werden .Ich spreche von niemand anderem als
swap
. Jeder Typ mit einer geeignetenswap
Funktion kann als intelligenter Verweis auf einen Inhalt verstanden werden, den er besitzt, bis der Besitz durch Austauschen auf eine andere Instanz desselben Typs übertragen wird. Jede Instanz behält ihre Identität, wird jedoch an neuen Inhalt gebunden. Es ist wie eine sicher wiederbindbare Referenz.(Es ist eher eine intelligente Referenz als ein intelligenter Zeiger, da Sie ihn nicht explizit dereferenzieren müssen, um an den Inhalt zu gelangen.)
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.quelle
Wenn der Ersteller des Objekts das Eigentum explizit an eine andere Person ü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.
quelle