2013-07-30 13 views
7

Was sind einige C 11 ++ std::unique_ptr Anwendungen und gotchas?Einige std :: unique_ptr Verwendungen und "Fallstricke"

Kann ich std::unique_ptr auch zum Speichern von dynamisch zugewiesenen Arrays verwenden?

Kann ich std::unique_ptr auch mit Ressourcen verwenden, die einen benutzerdefinierten Löschmechanismus verwenden?

Antwort

18

Lassen Sie uns einige Verwendungen und Probleme mit einem Q & A-Format organisieren.


Q1: Ich möchte zum Speichern eines Zeiger zu einer Klasse Component in meiner Klasse X.
Ich möchte nicht die "Container" -Klasse X kopierbar sein; X ist der einzige Besitzer von Component Instanzen.
Ich weiß, dass besitzenroh Zeiger eine schlechte Sache und mögliche Quellen von „leaktrocities“ sind (statt beobachtet rohe Zeiger sind in Ordnung). Was Smart-Zeiger könnte ich für diesen Zweck verwenden?

A1: C++ 11 des std::unique_ptr ist sicherlich eine gute Wahl.

Es ist gut, in Fällen von einzigartigen (nicht-shared) Eigentum, und nicht über den Overhead von std::shared_ptr.
Es ist ein ausgezeichneter Ersatz für vorherige C++ 98/03 boost::scoped_ptr.
In der Tat bietet std::unique_ptrBewegung Semantik.
Also, wenn Klasse X enthält unique_ptr<Component> Datenelemente (und andere bewegliche Datenelemente), wird die gesamte Klasse Xautomatisch beweglich sein.

Ein Beispiel für die Verwendung ist unten gezeigt:

#include <memory> // for std::unique_ptr 

class X 
{ 
    std::unique_ptr<Component> m_pComponent; 
    .... 

public: 
    X() 
     : m_pComponent(new Component()) 
    { 
     .... 
    } 
} 

(. Natürlich ist ein Smart Zeiger, gibt es keine Notwendigkeit, explizit in der destructor enthaltenden Klasse löschen)


Q2: Das ist großartig! Keine Notwendigkeit für explizite Destruktor, keine std::shared_ptr Overhead (typische C++ Philosophie: "Wir zahlen nicht für Dinge, die wir nicht verwenden"), bewegen Semantik-Maschinen bereits implementiert!
Allerdings habe ich ein Problem: meine Klasse Component hat eine Konstruktorüberladung, die einige Parameter, die ich im Konstruktorcode vor dem Erstellen von Component Instanzen berechnen muss, dauert.Ich habe versucht, in Konstruktor gewöhnlichen operator= assigment mit den neu geschaffenen Component zum unique_ptr zuweisen, aber ich bekam eine Fehlermeldung:

X::X() 
{ 
    .... 

    const int param = CalculateCoolParameter(); 

    // This assignment fails: 
    m_pComponent = new Component(param); // <---- Error pointing to '=' here 
       ^--- error 
} 

A2: OK, haben Sie wahrscheinlich eine operator= Überlastung erwartet hatten die vorherige Freigabe im Besitz Zeiger (falls vorhanden) und Zuweisung zu dem neu erstellten.
Leider gibt es keine solche Überlastung.
Allerdings wird die std::unique_ptr::reset() Methode tun!

m_pComponent.reset(new Component(param)); 

Q3: Hey! Diese unique_ptr ist wirklich cool! Ich mag die Tatsache, dass es schlau ist, es ist automatisch beweglich und bringt keinen Overhead.
Also würde ich es gerne verwenden, um eine dynamisch zugewiesenen Array von einiger konstanter Größe (berechnet zur Laufzeit) statt std::vector zu speichern (in diesem Teil des Codes bin ich stark eingeschränkt und ich nicht will für die std:vector Overhead bezahlen, da ich nicht alle std::vector dynamisch Größe ändern Funktionen, Deep-Copy, etc.) wollen.

Ich habe versucht, so etwas wie dieses:

const size_t count = GetComponentsCount(); 
unique_ptr<Component> components(new Component[count]); 

Es kompiliert gut, aber ich stellte fest, dass ~Component destructor nur einmal genannt wird, statt count destructor Anrufe ich erwartet hatte! Was läuft hier falsch?

A3: Das Problem ist, dass mit der obigen Syntax, std::unique_ptr verwendet delete die zugeordneten Objekte freizugeben. Da diese jedoch unter Verwendung von new[] zugeteilt wurden, ist der richtige Bereinigungsaufruf delete[] (nicht der einfache delete ohne Klammern).

unique_ptr<Component[]> components(new Components[count]); 
//     ^^ 
// 
// Note brackets "[]" after the first occurrence of "Component" 
// in unique_ptr template argument. 
// 

Q4: Das ist großartig

das zu beheben und unique_ptr zu instruieren richtig delete[] für die Freigabe Ressourcen zu verwenden, muss die folgende Syntax verwendet werden! Aber kann ich unique_ptr auch in Fällen, in denen der Code Ressourcenfreigabe ist nicht mit normaler C durchgeführt ++ delete (oder delete[]), sondern mit einiger benutzerdefinierten Bereinigungsfunktion, wie fclose() für C <stdio.h> Dateien (geöffnet mit fopen()) oder CloseHandle() für Win32-Datei HANDLE s (erstellt mit CreateFile())?

A4: Das ist auf jeden Fall möglich: Sie benutzerdefinierte deleter für std::unique_ptr angeben.

z.B.:

// 
// Custom deleter function for FILE*: fclose(). 
// 
std::unique_ptr<FILE,   // <-- the wrapped raw pointer type: FILE* 
       int(*)(FILE*)> // <-- the custom deleter type: fclose() prototype 
myFile(fopen("myfile", "rb"), // <-- resource (FILE*) is returned by fopen() 
     fclose);    // <-- the deleter function: fclose() 



// 
// Custom deleter functor for Win32 HANDLE: calls CloseHandle(). 
// 
struct CloseHandleDeleter 
{ 
    // The following pointer typedef is required, since 
    // the raw resource is HANDLE (not HANDLE*). 
    typedef HANDLE pointer; 

    // Custom deleter: calls CloseHandle(). 
    void operator()(HANDLE handle) const 
    { 
     CloseHandle(handle); 
    } 
}; 

std::unique_ptr<HANDLE, CloseHandleDeleter> myFile(CreateFile(....)); 
+0

FWIW, was bezahlen Sie für die Vektorfunktionen, die Sie nicht verwenden? (Hinweis: nichts) –

+0

Zumindest aus einem Speicher-Footprint kann 'std :: vector' 3 Zeiger verwenden, statt 'unique_ptr' nur einen. –

+0

A2: eine bessere Lösung, wenn möglich, ist eine Methode, die die Berechnung durchführt und die std :: unique_ptr zurückgibt, und diese dann in der Initialisierungsliste verwendet. – stijn