c++ smart - Was ist ein intelligenter Zeiger und wann sollte ich einen verwenden?




pointer c++11 (13)

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


Answers

Ich möchte einen weiteren Punkt zu der obigen Frage hinzufügen, Smart-Zeiger std :: shared_ptr hat keinen tiefgestellten Operator und unterstützt keine ponter-Arithmetik, wir können get () verwenden, um einen eingebauten Zeiger zu erhalten.


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

Intelligente Zeiger sollten gegenüber rohen Zeigern bevorzugt werden. Wenn Sie glauben, dass Sie Zeiger verwenden müssen (zuerst überlegen, ob Sie das wirklich tun), würden Sie normalerweise einen intelligenten Zeiger verwenden, da dies viele der Probleme mit unformatierten Zeigern lindern kann, hauptsächlich das Löschen des Objekts und das Lecken von Speicher.

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 durch Vergleich definiert eine Richtlinie, wann das Objekt zerstört wird. Sie müssen das Objekt noch erstellen, aber Sie müssen sich nicht länger 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, wie es durch boost::scoped_ptr oder std::unique_ptr implementiert wird.

void f()
{
    {
       boost::scoped_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // boost::scopted_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 scoped_ptr Instanzen nicht kopiert werden können. Dies verhindert, dass der Zeiger mehrmals (falsch) gelöscht wird. Sie können jedoch Referenzen zu anderen aufgerufenen Funktionen übergeben.

Bereichszeiger sind nützlich, wenn Sie die Lebensdauer des Objekts an einen bestimmten Codeblock binden möchten oder wenn Sie ihn als Elementdaten in ein anderes Objekt eingebettet haben, also die Lebensdauer dieses anderen Objekts. Das Objekt existiert, bis der umschließende Codeblock verlassen wird oder bis das umschließende Objekt selbst zerstört wird.

Eine komplexere Smart-Pointer-Richtlinie beinhaltet das Zählen des Zeigers. Dadurch kann der Zeiger kopiert werden. Wenn die letzte "Referenz" zum Objekt zerstört ist, wird das Objekt gelöscht. Diese Richtlinie wird von boost::shared_ptr und std::shared_ptr implementiert.

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.

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

Es gibt einen Nachteil, gezählte Zeiger zu referenzieren - die Möglichkeit, eine hängende Referenz zu erzeugen:

// 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 weitere Möglichkeit ist das Erstellen von Zirkelverweisen:

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

boost::shared_ptr<Owner> p1 (new Owner());
boost::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 weak_ptr , haben sowohl Boost als auch C ++ 11 ein weak_ptr definiert, um einen schwachen (nicht nummerierten) Verweis auf ein shared_ptr zu definieren.

AKTUALISIEREN

Diese Antwort ist ziemlich alt und beschreibt so, was zu der Zeit "gut" war, nämlich die von der Boost-Bibliothek bereitgestellten intelligenten Zeiger. Seit C ++ 11 hat die Standardbibliothek genügend intelligente Zeigertypen zur Verfügung gestellt, und daher sollten Sie die Verwendung von std::unique_ptr , std::shared_ptr und std::weak_ptr .

Es gibt auch std::auto_ptr . Es ist sehr ähnlich wie ein Bereichszeiger, außer dass es auch die "spezielle" gefährliche Fähigkeit hat, kopiert zu werden - die auch unerwartet Besitz überträgt! Es ist in den neuesten Standards veraltet, also sollten Sie es nicht verwenden. Verwenden Sie stattdessen std::unique_ptr .

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.

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 * überlädt. Das bedeutet, dass Ihr Objekt semantisch wie ein Zeiger aussieht, aber Sie können es viel cooler machen, einschließlich Referenzzählung, automatischer Zerstörung usw. shared_ptr und auto_ptr reichen in den meisten Fällen aus, kommen aber mit ihren eigenen kleinen Idiosynkrasien.


Intelligente Zeiger sind diejenigen, in denen Sie sich keine Gedanken über Speicher-Allokation, Ressourcen-Sharing und Transfer machen müssen.

Sie können diesen Zeiger sehr ähnlich wie jede Zuweisung in Java verwenden. In Java erledigt Garbage Collector den Trick, während in Smart Pointers der Trick von Destruktoren erledigt wird.


Ein intelligenter Zeiger ist eine Klasse, ein Wrapper eines normalen Zeigers. Im Gegensatz zu normalen Zeigern basiert der Lebenskreis des Smartpoints auf einer Referenzzahl (wie oft das Smart Pointer-Objekt zugewiesen wird). Wenn also ein intelligenter Zeiger einem anderen zugewiesen wird, wird der interne Referenzzähler plus Plus angezeigt. Und immer, wenn das Objekt außerhalb des Geltungsbereichs liegt, wird der Referenzzähler minus minus angezeigt.

Der automatische Zeiger ist, obwohl er ähnlich aussieht, völlig anders als der intelligente Zeiger. Es ist eine praktische Klasse, die die Ressource aufhebt, wenn ein automatisches Zeigerobjekt den Variablenbereich verlässt. Zu einem gewissen Grad bewirkt es, dass ein Zeiger (auf dynamisch zugewiesenen Speicher) ähnlich wie eine Stapelvariable arbeitet (statisch in der Kompilierungszeit zugeordnet).


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

In der Informatik ist ein intelligenter Zeiger ein abstrakter Datentyp, der einen Zeiger simuliert und zusätzliche Funktionen bereitstellt, z. B. automatische Speicherbereinigung oder Bereichsüberprüfung. Diese zusätzlichen Funktionen sollen Fehler reduzieren, die durch den Missbrauch von Zeigern verursacht werden, während die Effizienz erhalten bleibt. Intelligente Zeiger verfolgen typischerweise die Objekte, die auf sie zum Zweck der Speicherverwaltung 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 ausgeführt werden muss, macht es sehr wahrscheinlich, dass Speicherlecks auftreten. Intelligente Zeiger versuchen, Speicherlecks zu verhindern, indem sie die Ressourcenfreigabe automatisch machen: Wenn der Zeiger auf ein Objekt (oder das letzte in einer Reihe von Zeigern) zerstört wird, beispielsweise weil er den Gültigkeitsbereich verlässt, wird auch das spitze Objekt zerstört.


Die meisten Arten von intelligenten Zeigern behandeln die Entsorgung des pointer-to-Objekts für Sie. Es ist sehr praktisch, weil Sie nicht mehr über die manuelle Entsorgung von Objekten nachdenken müssen.

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

shared_ptr ist sehr vielseitig und behandelt eine Vielzahl von Entsorgungsszenarien, einschließlich Fällen, in denen Objekte über DLL-Grenzen hinweg weitergegeben werden müssen (der übliche Albtraumfall, wenn zwischen Ihrem Code und den DLLs unterschiedliche libc s verwendet werden).


Hier ist eine einfache Antwort für diese Tage von modernem C ++:

  • Was ist ein intelligenter Zeiger?
    Es ist eine Art von Wert, der wie ein Zeiger verwendet werden kann, bietet aber die zusätzliche Funktion der automatischen Speicherverwaltung: Wenn der Zeiger nicht mehr verwendet wird, wird der zugeordnete Speicher freigegeben (siehe auch die detailliertere Definition auf Wikipedia ).
  • Wann sollte ich einen verwenden?
    In einem Code, der das Verfolgen des Besitzes eines Stücks Speicher beinhaltet, Zuweisen oder Entfernen von Zuweisungen; Der intelligente Zeiger erspart Ihnen oft die Notwendigkeit, diese Dinge explizit zu tun.
  • Aber welcher intelligente Zeiger sollte ich in welchen Fällen verwenden?
    • Verwenden Sie std::unique_ptr wenn Sie nicht mehrere Referenzen auf dasselbe Objekt halten std::unique_ptr . Zum Beispiel, verwenden Sie es für einen Zeiger auf den Speicher, der bei der Eingabe eines Bereichs zugeordnet und beim Verlassen des Bereichs freigegeben wird.
    • Verwenden Sie std::shared_ptr wenn Sie von mehreren Stellen aus auf Ihr Objekt verweisen möchten - und nicht, dass die Zuweisung aufgehoben wird, bis alle diese Referenzen selbst entfernt wurden.
    • Verwenden Sie std::weak_ptr wenn Sie von mehreren Stellen aus auf Ihr Objekt verweisen möchten - für die Referenzen, für die Sie die Zuweisung ignorieren und rückgängig machen können (sie merken also, dass das Objekt verschwunden ist, wenn Sie die Dereferenzierung versuchen).
    • Verwenden Sie nicht die boost:: smart-Zeiger oder std::auto_ptr außer in speziellen Fällen, in denen Sie nachlesen können, wenn Sie müssen.
  • Hey, ich habe nicht gefragt, welchen man benutzen soll!
    Ah, aber du wolltest wirklich, gib es zu.
  • Wann sollte ich also normale Zeiger verwenden?
    Meistens in Code, der Gedächtnisbesitz nicht beachtet. Dies würde typischerweise in Funktionen geschehen, die einen Zeiger von einer anderen Stelle erhalten und keine Kopie des Zeigers zuordnen, freigeben oder speichern, die ihre Ausführung überdauert.

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

Man kann seinen eigenen intelligenten Zeiger implementieren, aber viele Bibliotheken stellen auch intelligente Zeigerimplementierungen bereit, die jeweils verschiedene Vor- und Nachteile haben.

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

  • shared_ptr<T> ist ein Zeiger auf T , der eine Referenzzahl verwendet, um festzustellen, wann das Objekt nicht mehr benötigt wird.
  • scoped_ptr<T> ist ein Zeiger, der automatisch gelöscht wird, wenn er den Gültigkeitsbereich scoped_ptr<T> . Keine Zuordnung ist möglich.
  • intrusive_ptr<T> ist ein weiterer Referenzzählzeiger. Es bietet eine bessere Leistung als shared_ptr , erfordert jedoch den Typ T , um einen eigenen Referenzzählungsmechanismus bereitzustellen.
  • weak_ptr<T> ist ein schwacher Zeiger, der in Verbindung mit shared_ptr , um zirkuläre Referenzen zu vermeiden.
  • 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 nur eine lineare Beschreibungen von jedem und kann nach Bedarf verwendet werden, für weitere Details und Beispiele kann man die Dokumentation von Boost betrachten.

Darüber hinaus stellt die C ++ - Standardbibliothek drei intelligente Zeiger bereit. std::unique_ptr für eindeutige Eigentümer, std::shared_ptr für gemeinsame Eigentümer und std::weak_ptr . std::auto_ptr existierte in C ++ 03, ist jetzt aber veraltet.


Sei T eine Klasse in diesem Tutorial Pointer in C ++ können in 3 Typen unterteilt werden:

1) Rohe Zeiger :

T a;  
T * _ptr = &a; 

Sie halten eine Speicheradresse an einen Ort im Speicher. Verwenden Sie mit Vorsicht, da Programme komplex werden schwer zu verfolgen.

Zeiger mit const 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 bedeutet, dass Sie den Datentyp nicht mit dem Zeiger ändern können. ie *ptr1 = 19 ; wird nicht funktionieren. Aber Sie können den Zeiger bewegen. dh ptr1++ , ptr1-- ; usw. wird funktionieren. Rückwärts gelesen: Zeiger auf Typ T, der const ist

  T * const ptr2 ;

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

const T * const ptr3 ; 

Ein const-Zeiger auf einen const-Datentyp T. Das bedeutet, dass Sie den Zeiger weder verschieben noch den Datentypzeiger als Zeiger festlegen können. dh. ptr3-- ; ptr3++ ; *ptr3 = 19; wird nicht funktionieren

3) Smart Pointer : { #include <memory> }

Gemeinsamer 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 mit Referenzzählung, um zu verfolgen, wie viele "Dinge" auf das Objekt zeigen, auf das der Zeiger zeigt. Wenn dieser Wert auf 0 gesetzt wird, wird das Objekt automatisch gelöscht, dh object wird gelöscht, wenn alle share_ptr, die auf das Objekt verweisen, den Gültigkeitsbereich verlassen. Dies beseitigt die Kopfschmerzen, Objekte löschen zu müssen, die Sie mit new zugewiesen haben.

Schwacher Zeiger: Hilft bei zyklischen Verweisen, die bei der Verwendung von Shared Pointer auftreten Wenn zwei Objekte auf zwei geteilte Zeiger zeigen und ein interner Shared Pointer auf den anderen Shared Pointer zeigt, dann gibt es einen zyklischen Verweis 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 einem shared_ptr zu weak_ptr. Hinweis: Um auf das Element zuzugreifen, auf das durch einen schwachen Zeiger verwiesen wird, verwenden Sie lock (), dies gibt ein weak_ptr zurück.

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 :: weak_ptr nützlich?

Unique Pointer: Leichter Smart Pointer mit exklusivem Besitz. Verwenden Sie, 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 Bewegungssemantik, um das Objekt zu ändern, auf das das eindeutige ptr verweist

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 sein, 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 Danke an Andre für das Aufzeigen dieser Frage.


Ein intelligenter Zeiger ist wie ein regulärer (typisierter) Zeiger, wie "char *", außer wenn der Zeiger selbst außerhalb des Gültigkeitsbereichs liegt, wird auch dessen Inhalt gelöscht. 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 neu zugeordnet werden müssen, die aber dieselbe Lebensdauer haben sollen wie etwas auf diesem Stapel. Wenn das Objekt einem intelligenten Zeiger zugewiesen ist, werden sie gelöscht, wenn das Programm diese Funktion / diesen Block verlässt.

  • Datenmitglieder von Klassen, so dass beim Löschen des Objekts alle eigenen Daten gelöscht werden, ohne speziellen Code im Destruktor (Sie müssen sicher sein, dass der Destruktor virtuell ist, was fast immer gut ist) .

In folgenden Fällen sollten Sie keinen intelligenten Zeiger verwenden:

  • ... der Zeiger sollte eigentlich nicht die Daten besitzen ... dh wenn Sie nur die Daten verwenden, aber Sie wollen, dass sie die Funktion überleben, auf die Sie verweisen.
  • ... der intelligente Zeiger wird nicht irgendwann selbst zerstört werden. Sie möchten nicht, dass sie im Speicher abgelegt wird, der niemals zerstört wird (z. B. in einem Objekt, das dynamisch zugewiesen wird, aber nicht explizit gelöscht wird).
  • ... zwei intelligente Zeiger könnten auf dieselben Daten zeigen. (Es gibt jedoch noch intelligentere Zeiger, die damit umgehen ... das heißt Referenzzählen .)

Siehe auch:


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 außer Kraft setzen. Sie haben eine Reihe von Vorteilen gegenüber regulären Zeigern. Sie werden garantiert als NULL-Zeiger oder als Zeiger auf ein Heap-Objekt initialisiert. Die Indirektion durch einen Nullzeiger wird überprüft. Kein Löschen ist jemals notwendig. Objekte werden automatisch freigegeben, wenn der letzte Zeiger auf sie verschwunden ist. Ein bedeutendes Problem mit diesen intelligenten Zeigern besteht darin, dass sie im Gegensatz zu regulären Zeigern die Vererbung nicht respektieren. Intelligente Zeiger sind für polymorphen Code unattraktiv. Im Folgenden finden Sie ein Beispiel für die Implementierung von Smartpointern.

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. Hier ist, wie man es benutzt:

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

Wie andere überladene Operatoren verhält sich p wie ein normaler Zeiger,

cout<<*p;
p->raise_salary(0.5);

Beantwortet das Ihre Frage?

Ich habe reinterpret_cast noch nie verwendet und frage mich, ob es nicht nach einem schlechten Design riecht, wenn man auf einen Fall trifft, der es braucht. In der Codebasis arbeite ich viel an dynamic_cast . Der Unterschied zu static_cast besteht darin, dass ein dynamic_cast Laufzeitüberprüfung durchführt, die (sicherer) oder auch nicht (mehr Aufwand) das ist, was Sie wollen (siehe msdn ).





c++ pointers c++11 smart-pointers c++-faq