Ich habe eine Klasse mit einem unique_ptr-Mitglied.
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
Die Leiste ist eine Klasse von Drittanbietern mit einer Funktion create () und einer Funktion destroy ().
Wenn ich a std::unique_ptr
damit in einer eigenständigen Funktion verwenden wollte, könnte ich Folgendes tun:
void foo() {
std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
...
}
Gibt es eine Möglichkeit, dies std::unique_ptr
als Mitglied einer Klasse zu tun ?
c++
c++11
move-semantics
unique-ptr
huitlarc
quelle
quelle
std::unique_ptr<Bar, decltype(&destroy)> ptr_;
unique_ptr
(alle müssen den Funktionszeiger zusammen mit dem Zeiger auf die tatsächlichen Daten speichern), die Zerstörungsfunktion jedes Mal übergeben muss und nicht inline sein kann (da die Vorlage dies nicht kann Spezialisieren Sie sich auf die spezifische Funktion (nur die Signatur) und müssen Sie die Funktion über den Zeiger aufrufen (teurer als direkter Aufruf). Beide rici und Deduplicator die Antworten vermeiden alle diese Kosten durch zu einem Funktor spezialisiert.Es ist möglich, dies sauber mit einem Lambda in C ++ 11 (getestet in G ++ 4.8.2) zu tun.
Angesichts dieser wiederverwendbaren
typedef
:Du kannst schreiben:
Zum Beispiel mit einem
FILE*
:Damit erhalten Sie die Vorteile einer ausnahmesicheren Bereinigung mit RAII, ohne dass Try / Catch-Rauschen erforderlich ist.
quelle
std::function
in der Definition oder ähnliches?std::function
. Lambda oder eine benutzerdefinierte Klasse wie in der akzeptierten Antwort können im Gegensatz zu dieser Lösung eingefügt werden. Dieser Ansatz hat jedoch Vorteile, wenn Sie die gesamte Implementierung in einem dedizierten Modul isolieren möchten.deleted_unique_ptr<Foo> foo(new Foo(), customdeleter);
wenn escustomdeleter
der Konvention folgt (es gibt void zurück und akzeptiert den Rohzeiger als Argument).Sie müssen nur eine Deleter-Klasse erstellen:
und geben Sie es als Vorlagenargument von an
unique_ptr
. Sie müssen das unique_ptr in Ihren Konstruktoren noch initialisieren:Soweit ich weiß, implementieren alle gängigen C ++ - Bibliotheken dies korrekt. da
BarDeleter
es eigentlich keinen zustand hat, muss es keinen platz in der besetzenunique_ptr
.quelle
struct BarDeleter
) bisstd::unique_ptr
(std::unique_ptr<Bar, BarDeleter>
), mit der derstd::unique_ptr
Konstruktor eine Deleter-Instanz selbst erstellen kann. dh der folgende Code ist erlaubtstd::unique_ptr<Bar, BarDeleter> bar[10];
typedef std::unique_ptr<Bar, BarDeleter> UniqueBarPtr
unique_ptr
, keine Notwendigkeit, beim Erstellen eine Instanz des Löschers bereitzustellen) und den Vorteil bietet, dass Sie ihnstd::unique_ptr<Bar>
überall verwenden können, ohne sich daran erinnern zu müssen um den speziellentypedef
oder explizit anbieter den zweiten vorlagenparameter zu verwenden. (Um klar zu sein, dies ist eine gute Lösung, ich habe dafür gestimmt, aber es bleibt einen Schritt vor einer nahtlosen Lösung stehen)Sofern Sie nicht in der Lage sein müssen, den Löscher zur Laufzeit zu ändern, würde ich dringend empfehlen, einen benutzerdefinierten Löschertyp zu verwenden. Wenn Sie beispielsweise einen Funktionszeiger für Ihren Deleter verwenden ,
sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*)
. Mit anderen Worten, die Hälfte der Bytes desunique_ptr
Objekts wird verschwendet.Das Schreiben eines benutzerdefinierten Löschers zum Umschließen jeder Funktion ist jedoch problematisch. Zum Glück können wir einen Typ schreiben, der für die Funktion vorgesehen ist:
Seit C ++ 17:
Vor C ++ 17:
quelle
deleter_from_fn
ist.Sie können einfach
std::bind
mit Ihrer Zerstörungsfunktion verwenden.Natürlich können Sie auch ein Lambda verwenden.
quelle
Sie wissen, dass die Verwendung eines benutzerdefinierten Löschers nicht der beste Weg ist, da Sie ihn in Ihrem gesamten Code erwähnen müssen.
Stattdessen können Sie Spezialisierungen zu Klassen auf Namespace-Ebene in hinzufügen
::std
solange benutzerdefinierte Typen beteiligt sind und Sie die Semantik respektieren, gehen Sie folgendermaßen vor:Spezialisiert
std::default_delete
:Und vielleicht auch
std::make_unique()
:quelle
std
eröffnet eine ganz neue Dose Würmer. Beachten Sie auch, dass die Spezialisierung vonstd::make_unique
nach C ++ 20 nicht zulässig ist (daher sollte dies vorher nicht erfolgen), da C ++ 20 die Spezialisierung von Dingenstd
nicht zulässt, bei denen es sich nicht um Klassenvorlagen handelt (std::make_unique
ist eine Funktionsvorlage). Beachten Sie, dass Sie wahrscheinlich auch mit UB enden werden, wenn der übergebene Zeigerstd::unique_ptr<Bar>
nicht voncreate()
, sondern von einer anderen Zuordnungsfunktion zugewiesen wurde.std::default_delete
die Anforderungen der Originalvorlage erfüllt. Ich würde mir vorstellen, dassstd::default_delete<Foo>()(p)
dies eine gültige Schreibweise wäre.delete p;
Wenndelete p;
also eine Schreibweise gültig wäre (dh wenn sieFoo
vollständig ist), wäre dies nicht dasselbe Verhalten. Wenndelete p;
das Schreiben ungültig war (Foo
unvollständig ist), würde dies außerdem ein neues Verhalten für angebenstd::default_delete<Foo>
, anstatt das Verhalten gleich zu halten.make_unique
Spezialisierung ist problematisch, aber ich habe definitiv diestd::default_delete
Überladung verwendet (nicht mit Vorlagen versehenenable_if
, nur für C-Strukturen wie OpenSSLsBIGNUM
, die eine bekannte Zerstörungsfunktion verwenden, bei der keine Unterklassifizierung stattfinden wird), und es ist bei weitem der einfachste Ansatz der Rest des Codes verwenden kann einfach ,unique_ptr<special_type>
ohne zu dem Funktors Typen passieren , wie das Templat amDeleter
ganzen Körper , noch Gebrauchtypedef
/using
einen Namen zu geben , um die Art , diese Frage zu vermeiden.