Schlaue Zeiger - Boost Smart Pointer



  • Inhalt

    Smart Pointer können komplexe Anwendungen in C++ wesentlich vereinfachen. Sie bieten eine automatische Speicherverwaltung ähnlich derer in restriktiveren Sprachen (z.B. VB, C#), sind aber auch in anderen Situationen hilfreich.

    Was sind Smart Pointer?

    Ein Smart Pointer verhält sich wie ein Zeiger, gibt das referenzierte Objekt aber automatisch frei, wenn es nicht mehr benötigt wird.

    "Nicht mehr benötigt" lässt sich in C++ schwer definieren. Daher gibt es verschiedene Smart Pointer-Implementationen, die auf die häufigsten Szenarien angepasst sind. Neben dem automatischen Freigeben sind natürlich noch andere Anwendungen möglich.

    Smart Pointer-Implementationen findet man in vielen Bibliotheken - jede mit ihren eigenen Vorzügen und Problemen. Dieser Artikel verwendet die Smart Pointer der Boost-Bibliotheken, einer hochwertigen OpenSource-Bibliothekssammlung, von denen einige für die Einbindung in den nächsten C++-Standard vorgesehen sind.

    Boost bietet die folgenden Implementationen:

    • shared_ptr<T>
      Zeiger auf T. Ein Referenzzähler bestimmt, wann das Objekt gelöscht werden muss shared_ptr ist der flexibelste Smart Pointer aus boost.
    • scoped_ptr<T>
      Zeiger auf T, das referenzierte Objekt wird gelöscht wenn der scoped_ptr den Gültigkeitsbereich verlässt. Gleiche Performance wie T *. Keine Zuweisung an andere scoped_ptr-Instanz möglich.
    • intrusive_ptr<T>
      Ein weiterer referenzgezählter Zeiger. Bessere Performance als shared_ptr, aber die Referenzzählung muss separat implementiert sein. Damit lassen sich "fremde" Referenzzähler (z.B. COM) unterbringen.
    • weak_ptr<T>
      Ein schwacher Zeiger, zusammen mit shared_ptr zum Aufbrechen zirkulärer Referenzen
    • shared_array<T>
      wie shared_ptr, aber Zugriff ist wie auf Array von T
    • scoped_array<T>
      wie scoped_ptr, aber Zugriff ist wie auf Array von T

    Ganz einfach: boost::scoped_ptr<T>

    scoped_ptr ist der einfachste Boost Smart Pointer. Er garantiert automatisches Löschen, wenn der Zeiger den Gültigkeitsbereich verlässt.

    ~Hinweis zum Beispielcode:
    Die Beispiele verwenden eine Hilfsklasse CSample, die für Konstruktion, Zuweisung usw. Meldungen ausgibt. Sicherlich ist es trotzdem interessant, die Beispiele unter dem Debugger nachzuvollziehen. Die Quellen enthalten die notwendigen Boost-Header (bitte "Boost installieren" weiter unten beachten!)~

    Beispiel 1 - mit normalen Zeigern

    void Sample1_Plain()
    {
      CSample * pSample(new CSample);
    
      if (!pSample->Query() ) // irgendeine Funktion...
      {
        delete pSample;
        return;
      }
    
      pSample->Use();
      delete pSample;
    }
    

    Beispiel 2 - mit scoped_ptr<T>

    #include "boost/smart_ptr.h"
    
    void Sample1_ScopedPtr()
    {
      boost::scoped_ptr<CSample> samplePtr(new CSample);
    
      if (!samplePtr->Query() ) // irgendeine Funktion...
        return;    
    
      samplePtr->Use();
    }
    

    Einen normalen Zeiger muss man an allen Stellen freigeben, an denen man die Funktion verlässt. Das ist leicht zu übersehen, wenn man Exceptions verwendet. Ein scoped_ptr wie im zweiten Beispiel wird automatisch freigegeben - auch wenn CSample::Query eine Exception wirft!

    Der Vorteil ist nicht zu übersehen: In einer komplexeren Funktion wird ein delete schnell vergessen - besonders, wenn man ein bisschen aufräumt.

    scoped_ptr eignet sich für: Automatische Freigabe von lokalen Objekten und Klassendaten, verzögerte Initialisierunng, Implementierung von PIMPL und RAII
    was geht nicht: Als Element in STL-Container, mehrere Zeiger auf das gleiche Objekt, andere Allokatoren als new/delete
    Performance: Praktisch identisch mit normalem Zeiger

    ~Tipp: Wer PIMPL (pointer to implementation, auch handle/body) und RAII (Resource Acquisition Is Initialization) nicht kennt, sollte ganz fix ein gutes C++-Buch zur Hand nehmen und diese wichtigen Konzepte nachholen. Smart Pointer sind nur eine (bequeme) Möglichkeit, diese zu implementieren.~

    scoped_ptr verbietet direkte implizite Zuweisungen:

    scoped_ptr<CSample> ptrA(new CSample);
    scoped_ptr<CSample> ptrB = ptrA; // Compile-Fehler!  
    ptrA = new CSample;              // Compile-Fehler!
    

    Das vermeidet die üblichen Fehler im Umgang mit Smart Pointern, die meistens am Übergang zwischen Smart Pointer und normalem Zeiger auftreten. Explizit darf man natürlich alles (und ist selbst schuld, wenn es schief geht):

    T* scoped_ptr<T>::get()      // gibt den enthaltenen Zeiger zurück
    scoped_ptr<T>::reset(T *)    // ersetzt den enthaltenen Zeiger mit einer neuen Instanz. 
                                 // die vorige Instanz wird dabei freigegeben
    

    Referenzzähler und shared_ptr<T>

    Wenn man mehrere Smart Pointer auf das gleiche Objekt zulassen möchte, benötigt man ein anderes Kriterium für "nicht mehr benötigt".
    Referenzgezählte Zeiger überwachen, wie viele Zeiger auf ein Objekt verweisen. Dazu wird jedem Objekt ein Zähler zugeordnet. Dieser wird bei einer Zeigerzuweisung erhöht, und im Zeigerdestruktor runtergezählt. Wenn der Zähler 0 erreicht, wird das Objekt gelöscht.

    Boost bietet shared_ptr als referenzgezählten Zeiger und "kleinen Alleskönner". Zuerst ein kleines Beispiel:

    void Sample2_Shared()
    {
      // (A) Erzeugen einer CSample-Instanz mit einer Referenz
      boost::shared_ptr<CSample> p1(new CSample); 
      printf("The Sample now has %i references\n", p1.use_count()); // use_count() == 1
    
      // (B) An zweiten Zeiger zuweisen:
      boost::shared_ptr<CSample> p2 = mySample; 
      printf("The Sample now has %i references\n", p2.use_count()); // use_count() == 2
    
      // (C) den ersten Zeiger auf NULL setzen
      p1.reset(); 
      printf("The Sample now has %i references\n", p2.use_count());  // use_count() == 1
    
      // Das bei (A) allozierte Objekt wird freigegeben, wenn p2 den Gültigkeitsbereich verlässt
    }
    

    Zeile (A) erzeugt eine CSample-Instanz auf dem Heap und speichert einen Zeiger darauf in dem shared_ptr mySample. Das sieht dann so aus:

    In (B) wird ein zweiter Zeiger auf das Objekt angelegt:

    mySample wird mittels reset() auf NULL gesetzt (reset() ist identisch p=NULL für einen normalen Zeiger). CSample wird noch von p2 referenziert:

    Erst wenn die letzte Referenz auf CSample verschwindet, wird CSample gelöscht:

    Häufige Anwendungsfälle sind:

    • p1 und p2 in obigem Beispiel haben voneinander unabhängige Lebensdauer
    • Container von Zeigern (z.B. ein std::vector< boost::shared_ptr<MyUncopyableClass> >)
    • PIMPL + RAII für von mehreren unabhängigen Clients genutzten Ressourcen / Objekten

    Beispiel für shared_ptr<T>: shared_ptr im STL-Container

    Viele Container-Klassen (u.a. STl-Container) erfordern Kopieroperationen, z.B. wenn ein existierendes Element in eine Liste oder einen Vektor eingefügt werden soll. Das ist für komplexe Klassen ungünstig und für manche Klassen sogar unmöglich. In diesem Fall weicht man üblicherweise auf einen Container von Zeigern aus:

    std::vector<CMyLargeClass *> vec;
    vec.push_back( new CMyLargeClass("dicke fette Zeichenkette") );
    

    Damit ist man aber als "Nutzer" wieder verantwortlich für die Speicherverwaltung. Mit einem shared_ptr wird es wieder einfacher:

    typedef boost::shared_ptr<CMyLargeClass>  CMyLargeClassPtr;
    std::vector<CMyLargeClassPtr> vec;
    vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );
    

    Sehr ähnlich, aber nun werden die Container-Elemente wieder automatisch mit dem Container freigegeben - es sei denn, es gibt noch einen anderen shared_ptr auf ein Element, wie im Folgenden demonstriert:

    void Sample3_Container()
    {
      typedef boost::shared_ptr<CSample> CSamplePtr;
    
      // (A) Vektor mit CSample-Zeigern:
      std::vector<CSamplePtr> vec;
    
      // (B) drei Elemente hinzufügen
      vec.push_back(CSamplePtr(new CSample("A"));
      vec.push_back(CSamplePtr(new CSample("B"));
      vec.push_back(CSamplePtr(new CSample("C"));
    
      // (C) Einen Zeiger auf das zweite Element "behalten": 
      CSamplePtr anElement = vec[1];
    
      // (D) den Vektor freigeben:
      vec.clear();  // "A" und "C" werden hier freigegeben
    
      // (E) das zweite Element "B" existiert noch: 
      anElement->Use();
      printf("fertig.\n");
    
      // (F) anElement verlässt den Gültigkeitsbereich und CSample("C") wird freigegeben
    }
    

    shared_ptr<T> - Die wichtigsten Eigenschaften

    Es gibt unzählige Smart Pointer-Implementationen. Einige wichtige Eigenschaften machen boost::shared_ptr empfehlenswert:

    • shared_ptr<T> kann für einen unvollständigen Typ T verwendet werden:
      class X; // forward declaration
      shared_ptr<X> pX; // "X *"
      Zum Dereferenzieren und Freigeben von pX muss X aber vollständig bekannt sein - boost prüft das und wirft andernfalls eine Compile-Fehlermeldung.
    • shared_ptr<T> kann man auf einen beliebigen Typen T anwenden:
      Es gibt praktisch keine Anforderungen an den Typen T (T muss z.B. nicht von einer vorgegebenen Basisklasse abgeleitet sein)
    • shared_ptr<T> erlaubt einen Custom Deleter
      Die automatische Freigabe des referenzierten Objekts erfolgt normalerweise mit delete. Man kann aber auch einen anderen Funktor angeben. Dadurch lassen sich beliebige Ressourcen in einem shared_ptr verwalten.
    • Der Deleter ist kein Template-Parameter der Klasse - dadurch wird man nicht zu weiteren Templates gezwungen (man kann void foo(shared_ptr<int>) statt template <typename D> void foo(shared_ptr<int,D>) verwenden)
    • Implizite Konvertierung:
      Wenn U * implizit in T * umgewandelt werden kann (z.B., weil T eine Basisklasse von U ist), kann ein shared_ptr<U> auch implizit in einen shared_ptr<T> umgewandelt werden.
    • shared_ptr ist threadsicher
      (Das ist zwar eher eine Entwurfsentscheidung als ein uneingeschränkter Vorteil, für Multithread-Programme aber unumgänglich und mit nur geringen Performance-Einbußen erkauft)
    • portabel, peer-reviewed, das Übliche.

    Was kann schief gehen?

    Zwar sind Smart Pointer um einiges robuster als gewöhnliche Zeiger, aber wie immer gibt es einige Dinge, die man unbedingt wissen muss:

    Regel 1: Zuweisen und Halten - Ein mit new erzeugtes Objekt sollte sofort an einen Smart Pointer zugewiesen und ausschließlich über diese referenziert werden. Die Smart Pointer sind nun Eigentümer des Objekts. Dadurch vermeidet man versehentliches vorzeitiges Löschen

    Regel 2: Ein _ptr<T> ist kein T * - genauer gesagt: es gibt keine implizite Konvertierung zwischen einem T * und einem shared_ptr<T>. Dadurch wird eine versehentliche Verletzung von Regel (1) ausgeschlossen. "Gefährliche" Operationen müssen daher explizit gemacht werden:

    • Ein Smart Pointer wird normalerweise mit ..._ptr<T> myPtr(new T) erzeugt.
    • Ein T * kann nicht an einen Smart Pointer zugewiesen werden.
    • Nicht einmal ptr=NULL ist erlaubt - stattdessen verwendet man ptr.reset().
    • Der normale Zeiger ist über ptr.get() verfügbar. Natürlich ist dieser Zeiger nur gültig, solange der entsprechende Smart Pointer existiert, und Freigeben per delete wäre fatal.
    • An eine Funktion, die einen _ptr<T> erwartet, kann ein T * nicht implizit übergeben werden. Stattdessen erstellt man einen lokalen Smart Pointer - damit gibt man den "Besitz" explizit ab. (Achtung! Regel 3!)
    • Es gibt keine allgemein gültige Methode, den Smart Pointer, der einen bestimmten normalen Zeiger enthält, aufzufinden, jedoch stellen die Boost: Smart Pointer Programming Techniques Lösungen für häufige Situationen dar.

    Regel 3: keine temporären shared_ptr - immer schön eine Variable vergeben. (Notwendig für korrekte Exception-Behandlung, Details findet man unter boost: shared_ptr best practices)

    Regel 4: Keine zirkulären Referenzen - mehr dazu sofort.
    Generell sind Boost Smart Pointer auf Sicherheit getrimmt - potenziell gefährliche Operationen müssen explizit geschrieben werden.

    Zyklische Referenzen

    Referenzzählung erlaubt bequemes und effektives automatisches Ressourcenmanagement, hat aber eine Schwachstelle: Objekte, die sich (direkt oder indirekt) gegenseitig referenzieren. Ein einfaches Beispiel:

    struct CDad;
    struct CChild;
    
    typedef boost::shared_ptr<CDad>   CDadPtr;
    typedef boost::shared_ptr<CChild> CChildPtr;
    
    struct CDad   : public CSample { CChildPtr myBoy;  };
    struct CChild : public CSample { CDadPtr myDad;    };
    
    CDadPtr   parent(new CDadPtr); 
    CChildPtr child(new CChildPtr);
    
    // absichtlich eine zirkuläre Referenz erstellen:
    parent->myBoy = child; 
    child->myDad = dad;
    
    // einen Zeiger zurücksetzen...
    child.reset();
    

    Sowohl die CChild-Instanz als auch parent referenzieren die CDad-Instanz, welche wiederum die CChild-instanz referenziert:

    Wenn man jetzt dad.reset() aufruft, verlieren die beiden Objekte jeglichen Kontakt zur Außenwelt, halten sich aber gegenseitig am Leben - ein klassisches Leck! Im schlimmsten Fall werden dadurch kritische Ressourcen nicht mehr freigegeben.

    Diese Problem ist nicht (bzw. nur mit inakzeptablen Restriktionen) innerhalb der shared_ptr-Implementation lösbar - man muss den Kreis erkennen und selbst auflösen. Dazu gibt es verschiedene Wege:

    • Bevor man die letzte Referenz freigibt, wird der Kreis manuell aufgelöst (z.B. dad.ReleaseChild(); dad.reset(); ).
    • Wenn Dad eine längere Lebensdauer als Child hat, kann CChild einen gewöhnlichen CDad * verwenden
    • man verwendet boost::weak_ptr für CChild.myDad

    Die Lösungen (1) und (2) können nicht immer eingesetzt werden, funktionieren aber mit allen Smart Pointer-Bibliotheken. (3) ist eigentlich eine Verallgemeinerung von (2), die aber ohne eine Anforderung an die Lebensdauer auskommt.

    Zyklische Referenzen mit weak_ptr aufbrechen

    Man unterscheidet starke und schwache Referenzen: Eine starke Referenz verhindert das Löschen eines Objekts, eine schwache Referenz tut dies nicht.
    In diesem Sinne ist boost::shared_ptr<T> eine starke und T * eine schwache Referenz. Mit T * kann man aber nicht prüfen, ob das Objekt noch existiert (daher die Lebensdauer-Anforderung in Lösung (2) oben).

    boost::weak_ptr<T> ist ein Smart Pointer, der eine schwache Referenz implementiert. Bei Bedarf kann man sich von einer solchen eine starke Referenz geben lassen. Falls das Objekt nicht mehr existiert, ist diese 0. Die starke Referenz darf natürlich nur solange wie nötig behalten werden.
    Obiges Beispiel mit eine weak_ptr:

    struct CBetterChild : public CSample
    {
      weak_ptr<CDad> myDad;
    
      void BringBeer()
      {
        shared_ptr<CDad> strongDad = myDad.lock(); // eine starke Referenz beantragen
        if (strongDad)                      // ist das Objekt noch existent?
          strongDad->SetBeer();
        // strongDad wird freigegeben, sobald sein Gültigkeitsbereich verlassen wird; die schwache Referenz wird behalten.
      }
    };
    

    Komplexe Objektstrukturen erfordern immer noch eine sorgfältige Analyse, an welchen Stellen zirkuläre Referenzen auftreten und welche davon durch einen weak_ptr aufgebrochen werden können (im Beispiel kann man z.B. nicht den Zeiger auf CChild durch einen weak_ptr ersetzen!) Hat man aber einmal eine ordentliche Struktur aufgebaut, muss man sich um die Freigabe keinen Kopf mehr machen - und gerade bei komplexen Strukturen ist das sehr angenehm.

    intrusive_ptr<t> - Das Leichtgewicht

    shared_ptr bietet eine Menge mehr als einen "normalen" Zeiger. Das hat natürlich einen kleine Preis: er ist größer, ein wenig langsamer, und benötigt zu jedem referenzierten Objekt ein separates "Tracking"-Objekt mit Zähler und Deleter. Das kann man fast immer vernachlässigen.

    Wenn nicht, bietet boost::intrusive_ptr eine interessante Alternative: Den "leichtmöglichsten" referenzgezählten Zeiger - allerdings muss man sich um das Zählen selbst kümmern. Das ist gar nicht so schlimm: will man seine eigenen Klassen Smart Pointer-kompatibel machen, packt man den Referenzzähler gleich mit rein. Dafür bekommt man weniger Allokationen und einen fixen Smart Pointer.

    Um einen Typ intrusive_ptr<T> zu verwenden, muss man zwei Funktionen definieren: intrusive_ptr_add_ref und intrusive_ptr_release. Das folgende Beispiel zeigt, wie:

    #include "boost/intrusive_ptr.hpp"
    
    class CRefCounted
    {
      private:
        long    references;
        friend void intrusive_ptr_add_ref(T * p);
        friend void intrusive_ptr_release(T * p);
    
      public:
        CRefCount() : references(0) {}   // references mit 0 initialisieren
    };
    
    // die zwei Funktionsüberladungen müssen bei den meisten Compilern im boost-Namespace stehen:
    namespace boost  
    {
      void intrusive_ptr_add_ref(CRefCounted * p)
      {
        // Referenzzähler für das Objekt *p inkrementieren
        ++(p->references);
      }
    
      void intrusive_ptr_release(CRefCounted * p)
      {
       // Referenzzähler dekrementieren und, wenn der Zähler 0 erreicht hat, Objekt löschen
       if (--(p->references) == 0)
         delete p;
      } 
    } // namespace boost
    

    Das ist die einfachste (und nicht threadsichere!) Implementation. Eine generische Implementation findet man nach kurzem Stöbern in der Boost Mailing List.

    Tipp:
    Um das Beispiel unter Windows/VC threadsicher zu bekommen, verwendet man Folgendes:

    extern "C" _InterlockedIncrement(LPLONG lpAddend);
    extern "C" _InterlockedDecrement(LPLONG lpAddend);
    #pragma intrinsic(_InterlockedIncrement, _InterlockedDecrement)  // werden inline expandiert!
    
    namespace boost
    {
      void intrusive_ptr_add_ref(CRefCounted * p)
    
      {
        _InterlockedIncrement(&(p->references));
      }
    
      void intrusive_ptr_release(CRefCounted * p)
      {
       if (_InterlockedDecrement(&(p->references)) == 0)
         delete p;
      } 
    }
    

    scoped_array und shared_array

    Die beiden sollen nicht unerwähnt bleiben - sie sind einem scoped_ptr bzw. shared_ptr sehr ähnlich, Syntax und Verhalten ähnelt aber einem mit new[] allozierten Zeiger (z.B. operator[], delete[] als Standard-Deleter). Hinweis: Keiner von beiden merkt sich die Länge, sie sind also als "Array-Ersatz" nur bedingt geeignet.

    Boost installieren

    Die aktuellen Boost-Bibliotheken können von boost.org heruntergeladen werden. Für die Verwendung der Smart Pointer-Bibliothek ist keine Übersetzung notwendig.
    Das Boost-Wurzelverzeichnis enthält Release Notes und eine Menge Verzeichnisse für Dokumentation, Build Tools usw. Die eigentlichen Quellen befinden sich in dem Unterverzeichnis boost\.

    Ich füge das boost-Wurzelverzeichnis zu den Standard-Include-Pfaden hinzu.
    in VC6: Extras/Einstellungen, "Verzeichnisse"-Reiter, "Verzeichnisse für... Include-Dateien"
    in VC7: Extras/Einstellungen, Projekte/VC++-Verzeichnisse, "Verzeichnisse für... Include-Dateien"

    Die #includes sehen dann so aus:

    #include <boost/smart_ptr.hpp>
    

    Was ist mit std::auto_ptr?

    std::auto_ptr ist der einzige Smart Pointer im aktuellen Standard. Leider - denn auto_ptr erfüllt zwar seine Zweck, ist aber unnötig komplex und wird schnell falsch eingesetzt.

    auto_ptr in zwei Sätzen: Die letzte auto_ptr-Instanz, der der Zeiger zugewiesen wurde, gibt das referenzierte Objekt frei. Das ist aber nicht notwendigerweise die letzte vorhandene auto_ptr-Instanz.

    In den beiden häufigsten Anwendungsfällen kann und sollte man auf andere Smart Pointer ausweichen:

    1) lokale Objekte / PIMPL ==> scoped_ptr

    class CBar;
    class CFoo
    {
       auto_ptr<CBar>   m_pBar;  // besser: scoped_ptr
       // ...
    }
    

    scoped_ptr verbietet die bei auto_ptr mögliche Zuweisung (die einen u.U. gefährlichen, weil versteckten Transfer of Ownership bedeutet)

    2) Rückgabewert aus Factory ==> shared_ptr

    class CBar;
    auto_ptr<CBar> MakeBar() { ... }  // besser: shared_ptr
    

    shared_ptr erlaubt die hier notwendige Zuweisung - und ist auch bei Mehrfachzuweisungen sicher. Das folgende Beispiel soll das illustrieren:

    auto_ptr<CFont> GetFont(EMyFavoriteFonts emff);
    ...
    auto_ptr<CFont> font = GetFont(emffCoolShadowedFont);
    widget1->font = font;  // ??
    widget2->font = font;  // ??
    

    Wer gibt das CFont-Objekt wieder frei? widget2 - selbst wenn widget1 noch existiert. Hier ist ein Referenzzähler sinnvoll - also verwendet man besser shared_ptr oder intrusive_ptr.

    Links (alle in Englisch)

    Boost Smart Pointer Bibliothek
    Smart Pointer - Tipps & Tricks
    Herb Sutter über Smart Pointers
    Originaler Artikel
    Kreativer Missbrauch von shared_ptr<T>

    _________________________________________________________________________________

    Sept 05, 2004: Erste Version
    Aug. 2005: Deutsche Übersetzung
    Jul. 2019 by Dravere: Link "Herb Sutter über Smart Pointers" aktualisiert



  • Vielleicht sollte man in dem Artikel erwähnen, dass die Boost-SmartPointer in den Standard TR1 aufgenommen wurden und daher von aktuellen Compilern direkt unterstützt werden.



  • Meiner MEinung nach haben sich zwei Fehler eingeschlichen
    Im Beispiel zu scoped_ptr:
    #include "boost/smart_ptr.h"
    muss es nicht
    #include "boost/smart_ptr.hpp"
    heissen? Jedenfalls in meiner Boost Version.

    Und beim Beispiel zu shared_ptr:

    void Sample2_Shared()
    {
      // (A) Erzeugen einer CSample-Instanz mit einer Referenz
      boost::shared_ptr<CSample> p1(new CSample);
      printf("The Sample now has %i references\n", p1.use_count()); // use_count() == 1
    
      // (B) An zweiten Zeiger zuweisen:
      boost::shared_ptr<CSample> p2 = mySample;
      printf("The Sample now has %i references\n", p2.use_count()); // use_count() == 2
    
      // (C) den ersten Zeiger auf NULL setzen
      p1.reset();
      printf("The Sample now has %i references\n", p2.use_count());  // use_count() == 1
    
      // Das bei (A) allozierte Objekt wird freigegeben, wenn p2 den Gültigkeitsbereich verlässt
    }
    

    Meint mySamle doch eher p1 oder?

    Aber auf jedenfall ein toller Artikel 👍



  • rüdiger schrieb:

    Vielleicht sollte man in dem Artikel erwähnen, dass die Boost-SmartPointer in den Standard TR1 aufgenommen wurden und daher von aktuellen Compilern direkt unterstützt werden.

    wie meinst Du das ?
    heisst das, dass man alle die oben erwähnte SmartPointer ohne der Boost-Bibliothek verwenden kann ?



  • Hem, also rüdigers Formulierung ist leicht irreführend, da sie vermuten lässt, das TR1 jedem Compiler beiliegt.

    Du kannst, wenn du dir eine TR1-Implementierung besorgst, diese in deinem aktuellen Compiler benutzen. Ich glaube der GCC hat als einziger Mainstream-Compiler TR1 dabei. Muß man mal die GCC-User fragen. Aber dann sieht das ganze im Code so aus:

    #include <memory>
    
    std::tr1:shared_ptr<int> int_p;
    

    Für MSVC u.a. Compiler kann man aber TR1 nachrüsten.



  • Artchi schrieb:

    Hem, also rüdigers Formulierung ist leicht irreführend, da sie vermuten lässt, das TR1 jedem Compiler beiliegt.

    Du kannst, wenn du dir eine TR1-Implementierung besorgst, diese in deinem aktuellen Compiler benutzen. Ich glaube der GCC hat als einziger Mainstream-Compiler TR1 dabei. Muß man mal die GCC-User fragen. Aber dann sieht das ganze im Code so aus:

    #include <memory>
    
    std::tr1:shared_ptr<int> int_p;
    

    Für MSVC u.a. Compiler kann man aber TR1 nachrüsten.

    danke für die Erklärung!

    aber dann habe ich noch eine Frage,
    wie rüste ich MSVC2005 Express Edition mit dem TR1 auf ?

    schöne Grüße,
    Sergius



  • Ja, wie gesagt, mußt du dir eine TR1-Implementierung besorgen. Entweder eine bei http://www.dinkumware.com kaufen (ist bisher die kompletteste auf dem Markt), oder eine kostenlose als Opensource downloaden.

    In Boost 1.34 ist zumindest eine unkomplette TR1-Implementierung vorhanden: http://www.boost.org/doc/html/boost_tr1.html Gut, es ist nur eine "Abbildung" auf Boost, aber solche Sachen wie Smartpointer, Function usw. sind drin und die benutze ich auch. Aber Hash-Container fehler da leider.



  • Schon ziemlich dreist, einen Artikel von codeproject.com 1:1 abtippen und dann nichtmal den Original Artikel erwähnen. 🙄



  • PlagiatsÜberführer schrieb:

    Schon ziemlich dreist, einen Artikel von codeproject.com 1:1 abtippen und dann nichtmal den Original Artikel erwähnen. 🙄

    Nicht, wenn's der selbe Autor ist 😉



  • Beim Beispiel zum shared_ptr wird zuletzt folgendes kommentiert:

    // (F) anElement verlässt den Gültigkeitsbereich und CSample("C") wird freigegeben
    

    Wurde nicht der Zeiger auf das zweite Element, also CSample("B") bis zuletzt aufgehoben?


Anmelden zum Antworten