Was ist ein intelligenter Zeiger und wann sollte ich einen verwenden?

1820

Was ist ein intelligenter Zeiger und wann sollte ich einen verwenden?

Alex Reynolds
quelle
7
Schauen Sie sich diese Frage an: <br> Smart Pointers: Oder wem gehört Ihr Baby
Martin York
2
Beachten Sie, dass die Implementierung von std :: auto_ptr in Visual Studio 2005 schrecklich fehlerhaft ist. <br> http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=98871 <br> http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=101842 Verwenden Sie die Boost stattdessen.
Richard
25
Zwei ausgezeichnete Artikel zu diesem Thema: - Intelligente Zeiger - Was, warum, welche? - Guru der Woche # 25
Lazer
1
Hier ist Alexandrescus (kostenloses) Kapitel über das Wesentliche beim Erstellen intelligenter Zeiger verschiedener Geschmacksrichtungen: informit.com/articles/article.aspx?p=31529 In seiner Implementierung verwendet er Vorlagenargumente als "Richtlinien", um anzugeben, welche Attribute er möchte ( zB Referenzzählung), während die Standardbibliothek separate Klassen verwendet. Beachten Sie, dass er auch schrieb, bevor rvalue-Referenzen verfügbar waren, um so etwas wie std :: unique_ptr zu ermöglichen.
Metal
Ich möchte der obigen Frage noch einen Punkt hinzufügen. Der intelligente Zeiger std :: shared_ptr hat keinen Indexoperator und unterstützt keine Ponterarithmetik. Wir können get () verwenden, um einen eingebauten Zeiger zu erhalten.
suresh m

Antworten:

1884

AKTUALISIEREN

Diese Antwort ist ziemlich alt und beschreibt daher, was zu dieser Zeit „gut“ war, nämlich intelligente Hinweise, die von der Boost-Bibliothek bereitgestellt wurden. Seit C ++ 11 bietet die Standardbibliothek ausreichend Smart-Pointer-Typen. Daher sollten Sie die Verwendung von std::unique_ptr, std::shared_ptrund bevorzugen std::weak_ptr.

Es gab auch std::auto_ptr. Es war einem Zeiger mit Gültigkeitsbereich sehr ähnlich, außer dass es auch die "besondere" gefährliche Fähigkeit hatte, kopiert zu werden - was auch unerwartet das Eigentum überträgt.
Es war in C ++ 11 veraltet und wurde in C ++ 17 entfernt , daher sollten Sie es nicht verwenden.

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

ALTE ANTWORT

Ein intelligenter Zeiger ist eine Klasse, die einen "rohen" (oder "nackten") C ++ - Zeiger umschließt, um die Lebensdauer des Objekts zu verwalten, auf das verwiesen wird. Es gibt keinen einzigen intelligenten Zeigertyp, aber alle versuchen, einen Rohzeiger auf praktische Weise zu abstrahieren.

Intelligente Zeiger sollten gegenüber rohen Zeigern bevorzugt werden. Wenn Sie der Meinung sind, dass Sie Zeiger verwenden müssen (überlegen Sie zuerst, ob Sie dies wirklich tun), möchten Sie normalerweise einen intelligenten Zeiger verwenden, da dies viele Probleme mit Rohzeigern lindern kann, hauptsächlich das Vergessen, das Objekt zu löschen und Speicher zu verlieren.

Bei rohen Zeigern muss der Programmierer das Objekt explizit zerstören, wenn es nicht mehr nützlich ist.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

Ein intelligenter Zeiger definiert im Vergleich eine Richtlinie darüber, wann das Objekt zerstört wird. Sie müssen das Objekt noch erstellen, müssen sich aber nicht mehr darum kümmern, es zu zerstören.

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

Die einfachste verwendete Richtlinie betrifft den Bereich des Smart-Pointer-Wrapper-Objekts, z. B. implementiert von boost::scoped_ptroder std::unique_ptr.

void f()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

Beachten Sie, dass std::unique_ptrInstanzen nicht kopiert werden können. Dies verhindert, dass der Zeiger mehrmals (falsch) gelöscht wird. Sie können jedoch Verweise darauf an andere von Ihnen aufgerufene Funktionen weitergeben.

std::unique_ptrs sind nützlich, wenn Sie die Lebensdauer des Objekts an einen bestimmten Codeblock binden möchten oder wenn Sie es als Elementdaten in ein anderes Objekt eingebettet haben, die Lebensdauer dieses anderen Objekts. Das Objekt existiert, bis der enthaltende Codeblock verlassen wird oder bis das enthaltende Objekt selbst zerstört wird.

Eine komplexere Smart-Pointer-Richtlinie umfasst das Referenzzählen des Zeigers. Dadurch kann der Zeiger kopiert werden. Wenn der letzte "Verweis" auf das Objekt zerstört wird, wird das Objekt gelöscht. Diese Richtlinie wird von boost::shared_ptrund implementiert std::shared_ptr.

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

Referenzzählzeiger sind sehr nützlich, wenn die Lebensdauer Ihres Objekts viel komplizierter ist und nicht direkt an einen bestimmten Codeabschnitt oder ein anderes Objekt gebunden ist.

Es gibt einen Nachteil bei referenzgezählten Zeigern - die Möglichkeit, eine baumelnde Referenz zu erstellen:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

Eine andere Möglichkeit ist das Erstellen von Zirkelverweisen:

struct Owner {
   std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

Um dieses Problem zu umgehen, haben sowohl Boost als auch C ++ 11 a definiert weak_ptr, um einen schwachen (nicht gezählten) Verweis auf a zu definieren shared_ptr.

Lloyd
quelle
7
Meinst du std::auto_ptr<MyObject> p1 (new MyObject());statt std::auto_ptr<MyObject> p1 (new Owner());?
Mateen Ulhaq
35
Tolle Antwort. Es wäre schön, wenn es für c ++ 11 aktualisiert würde. Ich fand diese Antwort auf der Suche nach Informationen über den neuen 11-Standard und es wäre schön, wenn zukünftige Besucher die aktualisierten Informationen finden könnten. Ich weiß, dass auto_ptr veraltet ist. Ich glaube, dass shated_ptr und schwaches_ptr wie beschrieben existieren, und ich denke, dass scoped_ptr jetzt im Standard unique_ptr ist. Wenn dies zutrifft, kann diese Antwort bitte aktualisiert werden?
SaulBack
16
Zu sagen, dass die Möglichkeit, eine baumelnde Referenz zu erstellen, ein Nachteil für Zeiger mit Referenzzählung ist, ist absolut verrückt. Mögliche baumelnde Referenzen sind ein Nachteil jedes C ++ - Zeigers . In der Tat ist es genau dieser Nachteil, den intelligente Zeiger lindern sollen .
Michael Dorst
16
Wenn Sie einen Zeiger auf einen intelligenten Zeiger deklarieren (wie im Beispiel), geben Sie wissentlich alle Vorteile des intelligenten Zeigers auf. Dies ist kein Nachteil oder Designfehler, sondern die idiotischste Verwendung, die man sich vorstellen kann.
Michael Dorst
3
A const std::auto_ptrist sicher zu verwenden, wenn Sie mit C ++ 03 nicht weiterkommen. Ich habe es ziemlich oft für Pimpl-Muster verwendet, bis ich Zugriff auf C ++ 11 bekam.
Toby Speight
303

Hier ist eine einfache Antwort für diese Tage des modernen C ++ (C ++ 11 und höher):

  • Was ist ein intelligenter Zeiger?
    Es ist ein Typ, dessen Werte wie Zeiger verwendet werden können, der jedoch die zusätzliche Funktion der automatischen Speicherverwaltung bietet: Wenn ein intelligenter Zeiger nicht mehr verwendet wird, wird der Speicher, auf den er verweist, freigegeben (siehe auch die detailliertere Definition auf Wikipedia ).
  • Wann sollte ich einen verwenden?
    In Code, der das Verfolgen des Eigentums an einem Speicherstück, das Zuweisen oder das Aufheben der Zuweisung umfasst; Der intelligente Zeiger erspart Ihnen häufig die explizite Ausführung dieser Aufgaben.
  • Aber welchen intelligenten Zeiger sollte ich in welchem ​​dieser Fälle verwenden?
    • Verwenden std::unique_ptrSie diese Option, wenn Sie nicht mehrere Verweise auf dasselbe Objekt enthalten möchten. Verwenden Sie es beispielsweise für einen Zeiger auf den Speicher, der beim Eingeben eines Bereichs zugewiesen und beim Verlassen des Bereichs freigegeben wird.
    • Verwenden std::shared_ptrSie diese Option, wenn Sie von mehreren Stellen aus auf Ihr Objekt verweisen möchten - und nicht möchten, dass die Zuordnung Ihres Objekts aufgehoben wird, bis alle diese Referenzen selbst verschwunden sind.
    • Verwenden std::weak_ptrSie diese Option, wenn Sie von mehreren Stellen aus auf Ihr Objekt verweisen möchten - für Referenzen, für die das Ignorieren und Freigeben in Ordnung ist (sie werden also nur feststellen, dass das Objekt beim Versuch der Dereferenzierung nicht mehr vorhanden ist).
    • Verwenden Sie keine boost::intelligenten Zeiger oder std::auto_ptraußer in besonderen Fällen, die Sie bei Bedarf nachlesen können.
  • Hey, ich habe nicht gefragt, welches ich verwenden soll!
    Ah, aber du wolltest es wirklich zugeben.
  • Wann sollte ich dann reguläre Zeiger verwenden?
    Meistens in Code, der den Speicherbesitz nicht wahrnimmt. Dies ist normalerweise in Funktionen der Fall, die einen Zeiger von einem anderen Ort erhalten und weder zuweisen noch die Zuordnung aufheben und keine Kopie des Zeigers speichern, die ihre Ausführung überdauert.
einpoklum
quelle
5
Es ist anzumerken, dass intelligente (besitzende) Zeiger zwar bei der ordnungsgemäßen Speicherverwaltung helfen, rohe (nicht besitzende) Zeiger jedoch für andere organisatorische Zwecke in Datenstrukturen nützlich sind. Herb Sutter hielt auf der CppCon 2016 eine großartige Präsentation zu diesem Thema, die Sie auf YouTube sehen können: Leak-Freedom in C ++ ... Standardmäßig.
wiktor.wandachowicz
1
@ wiktor.wandachowicz T*ist zu std::unique_ptr<T>was std::weak_ptr<T>ist zustd::shared_ptr<T>
Caleth
@ Caleth: Nein, das würde ich nicht sagen.
Einpoklum
1
@ TonyTannous: Mit Respekt - Es war eine große Bearbeitung; und ich fühle nicht, dass meine Antwort, die abstrakt ist, sie braucht. Ich schlage vor, dass Sie dem Beispiel eine separate Antwort geben, indem Sie in einem Kommentar darauf verweisen.
Einpoklum
112

Smart Pointer ist ein zeigerähnlicher Typ mit einigen zusätzlichen Funktionen, z. B. automatische Speicherfreigabe, Referenzzählung usw.

Ein kleines Intro finden Sie auf Seite Smart Pointers - Was, warum, was? .

Einer der einfachen Smart-Pointer-Typen ist std::auto_ptr(Kapitel 20.4.5 des C ++ - Standards), der es ermöglicht, Speicher automatisch freizugeben, wenn er außerhalb des Gültigkeitsbereichs liegt, und der robuster ist als die einfache Zeigerverwendung, wenn Ausnahmen ausgelöst werden, obwohl er weniger flexibel ist.

Ein weiterer praktischer Typ ist boost::shared_ptrdie Implementierung der Referenzzählung und die automatische Freigabe des Speichers, wenn keine Referenzen auf das Objekt mehr vorhanden sind. Dies hilft, Speicherlecks zu vermeiden, und ist einfach zu implementieren, um RAII zu implementieren .

Das Thema wird im Buch "C ++ - Vorlagen: Der vollständige Leitfaden" von David Vandevoorde, Nicolai M. Josuttis , Kapitel Kapitel 20 ausführlich behandelt. Intelligente Zeiger. Einige behandelte Themen:

sergtk
quelle
2
Die Warnung std::auto_ptrist veraltet und wird dringend empfohlen, da Sie versehentlich das Eigentum übertragen können. - C ++ 11 beseitigt die Notwendigkeit von Boost, benutze : std::unique_ptr, std::shared_ptrundstd::weak_ptr
ninMonkey
42

Die Definitionen von Chris, Sergdev und Llyod sind korrekt. Ich bevorzuge jedoch eine einfachere Definition, um mein Leben einfach zu halten: Ein intelligenter Zeiger ist einfach eine Klasse, die die Operatoren -> und überlastet *. Das bedeutet, dass Ihr Objekt semantisch wie ein Zeiger aussieht, Sie es jedoch viel cooler machen können, einschließlich Referenzzählung, automatischer Zerstörung usw., shared_ptrund auto_ptrin den meisten Fällen ausreichend sind, aber mit ihren eigenen kleinen Eigenheiten einhergehen.

Sridhar Iyer
quelle
30

Ein intelligenter Zeiger ist wie ein normaler (typisierter) Zeiger wie "char *", außer wenn der Zeiger selbst den Gültigkeitsbereich verlässt, wird auch das gelöscht, auf das er zeigt. Sie können es wie einen normalen Zeiger verwenden, indem Sie "->" verwenden, aber nicht, wenn Sie einen tatsächlichen Zeiger auf die Daten benötigen. Dafür können Sie "& * ptr" verwenden.

Es ist nützlich für:

  • Objekte, die mit new zugewiesen werden müssen, aber die gleiche Lebensdauer wie etwas auf diesem Stapel haben sollen. Wenn das Objekt einem intelligenten Zeiger zugewiesen ist, werden sie gelöscht, wenn das Programm diese Funktion / diesen Block verlässt.

  • Datenelemente von Klassen, sodass beim Löschen des Objekts auch alle eigenen Daten ohne speziellen Code im Destruktor gelöscht werden (Sie müssen sicherstellen, dass der Destruktor virtuell ist, was fast immer eine gute Sache ist). .

Möglicherweise möchten Sie keinen intelligenten Zeiger verwenden, wenn:

  • ... der Zeiger sollte eigentlich nicht die Daten besitzen ... dh wenn Sie nur die Daten verwenden, aber möchten, dass sie die Funktion überleben, auf die Sie sie verweisen.
  • ... der intelligente Zeiger selbst wird nicht irgendwann zerstört. Sie möchten nicht, dass es sich in einem Speicher befindet, der niemals zerstört wird (z. B. in einem Objekt, das dynamisch zugewiesen, aber nicht explizit gelöscht wird).
  • ... zwei intelligente Zeiger können auf dieselben Daten verweisen. (Es gibt jedoch noch intelligentere Zeiger, die damit umgehen ... das wird als Referenzzählung bezeichnet .)

Siehe auch:

Märkte
quelle
18

Die meisten Arten von intelligenten Zeigern übernehmen die Entsorgung des Zeigerobjekts für Sie. Dies ist sehr praktisch, da Sie nicht mehr daran denken müssen, Objekte manuell zu entsorgen.

Die am häufigsten verwendeten intelligenten Zeiger sind std::tr1::shared_ptr(oder boost::shared_ptr) und seltener std::auto_ptr. Ich empfehle die regelmäßige Verwendung von shared_ptr.

shared_ptr ist sehr vielseitig und befasst sich mit einer Vielzahl von Entsorgungsszenarien, einschließlich Fällen, in denen Objekte "über DLL-Grenzen hinweg übergeben werden müssen" (der übliche Albtraumfall, falls abweichend) libc zwischen Ihrem Code und den DLLs s verwendet werden).

Chris Jester-Young
quelle
18

Ein intelligenter Zeiger ist ein Objekt, das sich wie ein Zeiger verhält, aber zusätzlich die Kontrolle über Konstruktion, Zerstörung, Kopieren, Verschieben und Dereferenzieren bietet.

Man kann seinen eigenen Smart Pointer implementieren, aber viele Bibliotheken bieten auch Smart Pointer-Implementierungen mit jeweils unterschiedlichen Vor- und Nachteilen.

Zum Beispiel - Boost bietet die folgenden Smart - Pointer - Implementierungen:

  • shared_ptr<T>ist ein Zeiger auf die TVerwendung eines Referenzzählers, um zu bestimmen, wann das Objekt nicht mehr benötigt wird.
  • scoped_ptr<T>ist ein Zeiger, der automatisch gelöscht wird, wenn er den Gültigkeitsbereich verlässt. Eine Zuordnung ist nicht möglich.
  • intrusive_ptr<T>ist ein weiterer Referenzzählzeiger. Es bietet eine bessere Leistung als shared_ptr, erfordert jedoch, dass der Typ Teinen eigenen Referenzzählmechanismus bereitstellt.
  • weak_ptr<T>ist ein schwacher Zeiger, der zusammen mit Zirkelverweisen arbeitet shared_ptr.
  • shared_array<T>ist wie shared_ptr, aber für Arrays von T.
  • scoped_array<T>ist wie scoped_ptr, aber für Arrays von T.

Dies sind jeweils nur eine lineare Beschreibung, die je nach Bedarf verwendet werden kann. Weitere Einzelheiten und Beispiele finden Sie in der Dokumentation von Boost.

Darüber hinaus bietet die C ++ - Standardbibliothek drei intelligente Zeiger. std::unique_ptrfür einzigartiges Eigentum, std::shared_ptrfür gemeinsames Eigentum und std::weak_ptr. std::auto_ptrexistierte in C ++ 03, ist aber jetzt veraltet.

Saqlain
quelle
Bitte erläutern Sie, warum scoped_ptres sich nicht um eine lokal deklarierte const unique_ptrDatei handelt, die auch beim Verlassen des Bereichs gelöscht wird.
Einpoklum
11

Hier ist der Link für ähnliche Antworten: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

Ein intelligenter Zeiger ist ein Objekt, das wie ein normaler Zeiger wirkt, aussieht und sich anfühlt, aber mehr Funktionalität bietet. In C ++ werden intelligente Zeiger als Vorlagenklassen implementiert, die einen Zeiger kapseln und Standardzeigeroperatoren überschreiben. Sie haben eine Reihe von Vorteilen gegenüber regulären Zeigern. Sie werden garantiert entweder als Nullzeiger oder als Zeiger auf ein Heap-Objekt initialisiert. Die Indirektion durch einen Nullzeiger wird überprüft. Es ist niemals ein Löschen erforderlich. Objekte werden automatisch freigegeben, wenn der letzte Zeiger auf sie verschwunden ist. Ein wesentliches Problem bei diesen intelligenten Zeigern besteht darin, dass sie im Gegensatz zu regulären Zeigern die Vererbung nicht berücksichtigen. Intelligente Zeiger sind für polymorphen Code unattraktiv. Im Folgenden finden Sie ein Beispiel für die Implementierung intelligenter Zeiger.

Beispiel:

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

Diese Klasse implementiert einen intelligenten Zeiger auf ein Objekt vom Typ X. Das Objekt selbst befindet sich auf dem Heap. So verwenden Sie es:

smart_pointer <employee> p= employee("Harris",1333);

Wie andere überladene Operatoren verhält sich p wie ein regulärer Zeiger.

cout<<*p;
p->raise_salary(0.5);
Santosh
quelle
9

http://en.wikipedia.org/wiki/Smart_pointer

In der Informatik ist ein intelligenter Zeiger ein abstrakter Datentyp, der einen Zeiger simuliert und gleichzeitig zusätzliche Funktionen wie die automatische Speicherbereinigung oder die Überprüfung von Grenzen bereitstellt. Diese zusätzlichen Funktionen sollen Fehler reduzieren, die durch den Missbrauch von Zeigern verursacht werden, während die Effizienz erhalten bleibt. Intelligente Zeiger verfolgen normalerweise die Objekte, die zum Zweck der Speicherverwaltung auf sie zeigen. Der Missbrauch von Zeigern ist eine Hauptursache für Fehler: Die ständige Zuweisung, Freigabe und Referenzierung, die von einem mit Zeigern geschriebenen Programm durchgeführt werden muss, macht es sehr wahrscheinlich, dass Speicherlecks auftreten. Intelligente Zeiger versuchen, Speicherlecks zu verhindern, indem sie die Freigabe von Ressourcen automatisch vornehmen: Wenn der Zeiger auf ein Objekt (oder der letzte in einer Reihe von Zeigern) zerstört wird,

Jorge Ferreira
quelle
6

In diesem Tutorial sei T eine Klasse. Zeiger in C ++ können in drei Typen unterteilt werden:

1) Rohzeiger :

T a;  
T * _ptr = &a; 

Sie halten eine Speicheradresse an einer Stelle im Speicher. Seien Sie vorsichtig, da Programme komplex werden und schwer zu verfolgen sind.

Zeiger mit konstanten Daten oder Adresse {Rückwärts lesen}

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

Zeiger auf einen Datentyp T, der eine Konstante ist. Das heißt, Sie können den Datentyp nicht mit dem Zeiger ändern. dh *ptr1 = 19; wird nicht funktionieren. Sie können den Zeiger jedoch bewegen. dh ptr1++ , ptr1--; etc wird funktionieren. Rückwärts lesen: Zeiger auf Typ T, der const ist

  T * const ptr2 ;

Ein konstanter Zeiger auf einen Datentyp T. Das heißt, Sie können den Zeiger nicht bewegen, aber Sie können den Wert ändern, auf den der Zeiger zeigt. dh *ptr2 = 19wird funktionieren, aber ptr2++ ; ptr2--etc wird nicht funktionieren. Rückwärts lesen: const Zeiger auf einen Typ T.

const T * const ptr3 ; 

Ein const-Zeiger auf einen const-Datentyp T. Das heißt, Sie können weder den Zeiger verschieben noch den Datentypzeiger als Zeiger ändern. dh. ptr3-- ; ptr3++ ; *ptr3 = 19;wird nicht funktionieren

3) Intelligente Zeiger : { #include <memory>}

Geteilter Zeiger :

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

Implementiert mithilfe der Referenzzählung, um zu verfolgen, wie viele "Dinge" auf das Objekt zeigen, auf das der Zeiger zeigt. Wenn diese Anzahl auf 0 geht, wird das Objekt automatisch gelöscht, dh das Objekt wird gelöscht, wenn alle auf das Objekt zeigenden share_ptr den Gültigkeitsbereich verlassen. Dadurch entfällt der Kopfschmerz, Objekte löschen zu müssen, die Sie mit new zugewiesen haben.

Schwacher Zeiger: Hilft beim Umgang mit zyklischen Referenzen, die bei Verwendung des gemeinsamen Zeigers auftreten. Wenn Sie zwei Objekte haben, auf die zwei gemeinsame Zeiger zeigen, und ein interner gemeinsamer Zeiger auf den gemeinsamen Zeiger des anderen zeigt, gibt es eine zyklische Referenz und das Objekt nicht gelöscht werden, wenn freigegebene Zeiger den Gültigkeitsbereich verlassen. Um dies zu lösen, ändern Sie das interne Mitglied von shared_ptr in schwach_ptr. Hinweis: Um mit lock () auf das Element zuzugreifen, auf das ein schwacher Zeiger zeigt, wird ein schwaches_ptr zurückgegeben.

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

Siehe: Wann ist std :: schwach_ptr nützlich?

Einzigartiger Zeiger: Leichter intelligenter Zeiger mit exklusivem Besitz. Verwenden Sie diese Option, wenn der Zeiger auf eindeutige Objekte zeigt, ohne die Objekte zwischen den Zeigern zu teilen.

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

Verwenden Sie die Verschiebungssemantik, um das Objekt zu ändern, auf das der eindeutige ptr zeigt

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

Referenzen: Sie können im Wesentlichen als const-Zeiger betrachtet werden, dh als Zeiger, der const ist und nicht mit besserer Syntax verschoben werden kann.

Siehe: Was sind die Unterschiede zwischen einer Zeigervariablen und einer Referenzvariablen in C ++?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

Referenz: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ Vielen Dank an Andre für den Hinweis auf diese Frage.

nnrales
quelle
3

Ein intelligenter Zeiger ist eine Klasse, ein Wrapper eines normalen Zeigers. Im Gegensatz zu normalen Zeigern basiert der Lebenskreis des Smart Points auf einem Referenzzähler (wie oft das Smart Pointer-Objekt zugewiesen wurde). Wenn also ein intelligenter Zeiger einem anderen zugewiesen wird, zählt die interne Referenz plus plus. Und wenn das Objekt den Gültigkeitsbereich verlässt, zählt die Referenz minus minus.

Der automatische Zeiger sieht zwar ähnlich aus, unterscheidet sich jedoch grundlegend vom intelligenten Zeiger. Es ist eine praktische Klasse, die die Zuordnung der Ressource aufhebt, wenn ein automatisches Zeigerobjekt den variablen Bereich verlässt. Bis zu einem gewissen Grad funktioniert ein Zeiger (auf dynamisch zugewiesenen Speicher) ähnlich wie eine Stapelvariable (statisch in der Kompilierungszeit zugewiesen).

Trombe
quelle
2

Intelligente Zeiger sind solche, bei denen Sie sich nicht um die Aufhebung der Speicherzuweisung, die gemeinsame Nutzung von Ressourcen und die Übertragung kümmern müssen.

Sie können diesen Zeiger sehr gut verwenden, ähnlich wie jede Zuordnung in Java funktioniert. In Java macht Garbage Collector den Trick, während in Smart Pointers der Trick von Destruktoren ausgeführt wird.

Daksh
quelle
1

Die vorhandenen Antworten sind gut, decken jedoch nicht ab, was zu tun ist, wenn ein intelligenter Zeiger nicht die (vollständige) Antwort auf das Problem ist, das Sie lösen möchten.

Unter anderem (in anderen Antworten gut erklärt) ist die Verwendung eines intelligenten Zeigers eine mögliche Lösung für Wie verwenden wir eine abstrakte Klasse als Funktionsrückgabetyp? die als Duplikat dieser Frage markiert wurde. Die erste Frage, die gestellt werden muss, wenn Sie versucht sind, eine abstrakte (oder tatsächlich eine beliebige) Basisklasse als Rückgabetyp in C ++ anzugeben, lautet "Was meinen Sie wirklich?". Es gibt eine gute Diskussion (mit weiteren Referenzen) über die idiomatische objektorientierte Programmierung in C ++ (und wie sich diese von anderen Sprachen unterscheidet) in der Dokumentation des Boost-Pointer-Container-Bibliothek. Zusammenfassend muss man in C ++ über das Eigentum nachdenken. Welche intelligenten Zeiger helfen Ihnen, sind aber nicht die einzige Lösung oder immer eine vollständige Lösung (sie geben Ihnen keine polymorphe Kopie) und sind nicht immer eine Lösung, die Sie in Ihrer Benutzeroberfläche verfügbar machen möchten (und eine Funktionsrückgabe klingt schrecklich viel wie eine Schnittstelle). Es kann beispielsweise ausreichend sein, eine Referenz zurückzugeben. In all diesen Fällen (Smart Pointer, Zeigercontainer oder einfache Rückgabe einer Referenz) haben Sie die Rückgabe von einem Wert in eine Form von geändert oder Referenz. Wenn Sie wirklich eine Kopie benötigen, müssen Sie möglicherweise mehr Boilerplate "idiom" hinzufügen oder über idiomatisches (oder anderweitiges) OOP in C ++ hinausgehen, um mithilfe von Bibliotheken wie Adobe Poly einen allgemeineren Polymorphismus zu erzielen Boost.TypeErasure erzielen .

da77a
quelle