zeiger - smart pointer c++11




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

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


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.


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).


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.


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.

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);

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.


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.


Smartpointer ist ein zeigerartiger Typ mit einigen zusätzlichen Funktionen, z. B. automatische Speicherfreigabe, Referenzzählung usw.

Kleines Intro ist auf der Seite Smart Pointer - Was, Warum, Was? .

Einer der einfachen Smart-Pointer-Typen ist std::auto_ptr (Kapitel 20.4.5 des C ++ - Standards), mit dem Speicher automatisch freigegeben werden kann, wenn er nicht mehr im Bereich liegt und der robuster ist als die einfache Pointer-Verwendung, wenn Ausnahmen ausgelöst werden flexibel.

Ein weiterer praktischer Typ ist boost::shared_ptr der die Referenzzählung implementiert und die Speicherfreigabe automatisch aufhebt, wenn keine Referenzen auf ein Objekt bestehen. Dies hilft Speicherverluste zu vermeiden und ist einfach zu implementieren RAII .

Das Thema wird ausführlich im Buch "C ++ Templates: The Complete Guide" von David Vandevoorde, Nicolai M. Josuttis , Kapitel 20 behandelt. Smart Pointers. Einige Themen behandelt:

  • Schutz vor Ausnahmen
  • Inhaber, (beachten Sie, std::auto_ptr ist die Implementierung eines solchen Typs von Smart Pointer)
  • Ressourcenerfassung wird initialisiert (Dies wird häufig für die ausnahmesichere Ressourcenverwaltung in C ++ verwendet)
  • Halter Einschränkungen
  • Referenzzählung
  • Gleichzeitiger Zählerzugriff
  • Zerstörung und Deallokation

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.







c++-faq