2013-09-27 5 views
80

Ich habe eine Klasse mit einem unique_ptr Mitglied.Wie benutze ich einen benutzerdefinierten Deleter mit einem std :: unique_ptr Mitglied?

class Foo { 
private: 
    std::unique_ptr<Bar> bar; 
    ... 
}; 

Der Balken ist eine Klasse von Drittanbietern, die über eine create() -Funktion und eine destroy() -Funktion verfügt.

Wenn ich ein std::unique_ptr mit ihm in einer Stand-alone-Funktion nutzen wollte ich tun konnte:

void foo() { 
    std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); }); 
    ... 
} 

Gibt es eine Möglichkeit, dies als Mitglied einer Klasse mit std::unique_ptr zu tun?

Antwort

78

Unter der Annahme, dass create und destroy sind frei Funktionen mit den folgenden Signaturen (was der Fall aus dem OP des Code-Schnipsel zu sein scheint):

Bar* create(); 
void destroy(Bar*); 

Sie Ihre Klasse Foo wie diese

class Foo { 

    std::unique_ptr<Bar, void(*)(Bar*)> ptr_; 

    // ... 

public: 

    Foo() : ptr_(create(), destroy) { /* ... */ } 

    // ... 
}; 
schreiben

Beachten Sie, dass Sie hier keinen Lambda oder benutzerdefinierten Deleter schreiben müssen, da destroy bereits ein Deleter ist.

+86

Mit C++ 11 'std :: unique_ptr ptr_;' – Joe

3

Sie können einfach std::bind mit einer Ihre zerstören Funktion verwenden.

std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy, 
    std::placeholders::_1)); 

Aber natürlich können Sie auch eine Lambda verwenden.

std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);}); 
33

Sie brauchen nur eine deleter Klasse zu erstellen:

struct BarDeleter { 
    void operator()(Bar* b) { destroy(b); } 
}; 

und bieten es als Template-Argument von unique_ptr. Sie werden immer noch die unique_ptr in Ihrer Konstruktoren müssen:

class Foo { 
    public: 
    Foo() : bar(create()), ... { ... } 

    private: 
    std::unique_ptr<Bar, BarDeleter> bar; 
    ... 
}; 

Soweit ich weiß, alle populären C++ Bibliotheken implementieren diese korrekt; seit BarDeleter hat eigentlich keinen Zustand, es braucht keinen Platz in der unique_ptr belegen.

+4

Diese Option ist die einzige, die mit Arrays, std :: vector und anderen Auflistungen arbeitet, da sie den Nullparameter std :: unique_ptr-Konstruktor verwenden kann.Andere Antworten verwenden Lösungen, die keinen Zugriff auf diesen Nullparameterkonstruktor haben, da eine Deleter-Instanz beim Erstellen eines eindeutigen Zeigers bereitgestellt werden muss. Diese Lösung bietet jedoch eine Deleter-Klasse ('struct BarDeleter') für' std :: unique_ptr' ('std :: unique_ptr '), die es dem Konstruktor 'std :: unique_ptr' ermöglicht, eine Deleter-Instanz selbst zu erstellen . dh der folgende Code ist erlaubt: 'std :: unique_ptr bar [10];' – DavidF

+5

Ich würde ein typedef für die einfache Benutzung erstellen. typedef std :: unique_ptr UniqueBarPtr' – DavidF

60

Es ist möglich, dies sauber mit einem Lambda in C++ 11 (getestet in G ++ 4.8.2) zu tun.

Angesichts dieser wieder verwendbaren typedef:

template<typename T> 
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>; 

Sie schreiben:

deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); }); 

Zum Beispiel mit einem FILE*:

deleted_unique_ptr<FILE> file(
    fopen("file.txt", "r"), 
    [](FILE* f) { fclose(f); }); 

Damit Sie erhalten die Vorteile der Ausnahme-sichere Reinigung mit RAII, ohne Versuch/catch-Lärm.

+1

Dies sollte die Antwort sein, imo. Es ist eine schönere Lösung. Oder gibt es irgendwelche Nachteile, wie z.B. haben 'std :: function' in der Definition oder dergleichen? – j00hi

+6

@ j00hi, meiner Meinung nach hat diese Lösung einen unnötigen Overhead wegen 'std :: function'. Lambda oder benutzerdefinierte Klasse wie in angenommenen Antwort kann anders als diese Lösung inline sein. Dieser Ansatz hat jedoch Vorteile, wenn Sie alle Implementierungen in einem dedizierten Modul isolieren möchten. – magras

+2

Dies wird Speicher verlieren, wenn std :: function-Konstruktor wirft (was passieren kann, wenn Lambda zu groß ist, um in std :: function-Objekt zu passen) – Ivan

Verwandte Themen