Wechselnde Deleter während der Lebensdauer eines unique_ptr (4/7)

Wechselnde Deleter während der Lebensdauer eines unique_ptr (4/7)

Eine vorherige Episode in der Intelligente Entwickler verwenden intelligente Zeiger Serie zeigte, wie (und warum) benutzerdefinierte Löscher in std::unique_ptr verwendet werden . Sehen wir uns nun die Methoden an, die den benutzerdefinierten Löscher während der Lebensdauer ändern von unique_ptr und auch von denen, die dies nicht tun. Auf diesen Aspekt von Smart Pointern wurde ich von Mathieu Ropert und Raoul Borges hingewiesen. Danke Jungs.

Die Reihe Smart developer use Smart pointers enthält derzeit:

  • Smart Pointer-Grundlagen
  • eindeutiger_ptr, gemeinsam genutzter_ptr, schwacher_ptr, bereichsbezogener_ptr, rohe Zeiger:Ihre Absichten klar darlegen, indem Sie Ihre intelligenten Zeiger kennen
  • Benutzerdefinierte Löscher und wie sie ausdrucksstärker werden
  • Wechsel von Löschern während der Lebensdauer eines unique_ptr
  • Wie man das Pimpl-Idiom mit Hilfe von unique_ptr implementiert
  • Wie man einen polymorphen Klon in modernem C++ erstellt
  • Wie man einen Smart Pointer zurückgibt UND Kovarianz verwendet (von Raoul Borges)

In den folgenden Fällen verwenden wir einen unique_ptr für einen Typ, der auf zwei verschiedene Arten zerstört werden kann. Um zu sehen, warum dies nützlich sein kann, sehen Sie sich den Beitrag an, der den benutzerdefinierten Löschern gewidmet ist.

Als Spielzeugbeispiel verwenden wir einen unique_ptr auf int , mit einem anpassbaren Löscher:

using IntDeleter = void(*)(int*);
using IntUniquePtr = std::unique_ptr<int, IntDeleter>;

Ein Löscher ist für gerade Zahlen und ein anderer für ungerade Zahlen zu verwenden:

void deleteEvenNumber(int* pi)
{
    std::cout << "Delete even number " << *pi << '\n';
    delete pi;
}

void deleteOddNumber(int* pi)
{
    std::cout << "Delete odd number " << *pi << '\n';
    delete pi;
}

Zuweisung von einem anderen std::unique_ptr

Betrachten Sie den folgenden Code:

IntUniquePtr p1(new int(42), deleteEvenNumber);
IntUniquePtr p2(new int(43), deleteOddNumber);
p1 = move(p2);

p1 , das eine gerade Zahl mit dem entsprechenden Löscher enthält, übernimmt den Besitz der Ressource in p2 . Die Frage ist:Wie wird es diese Ressource zerstören? Wird es den Löscher verwenden, mit dem es erstellt wurde, oder eher den Löscher von p2 übernehmen zusammen mit dem Eigentümer seiner Ressource?

Nehmen Sie sich einen Moment Zeit, um darüber nachzudenken, und klicken Sie dann unten, um herauszufinden, was dieses Programm ausgibt (die Löscher drucken die Informationen aus – sehen Sie sich ihren Code oben im Artikel an):

Delete even number 42
Delete odd number 43

Jede Ressource wird mit dem richtigen Löscher gelöscht, was bedeutet, dass die Zuweisung den Löscher gebracht hat. Dies ist sinnvoll, da die Ressourcen sonst nicht mit dem richtigen Löschprogramm entsorgt würden.

Zeiger zurücksetzen

Eine andere Möglichkeit, die in einem std::unique_ptr enthaltene Ressource zu ändern ist, seine reset zu nennen Methode, wie im folgenden einfachen Beispiel:

std::unique_ptr<int> p1(new int(42));
p1.reset(new int(43));

Die reset -Methode ruft den Deleter für die aktuelle Ressource auf (42) und übernimmt dann die neue (43).

Aber die reset Methode akzeptiert nur ein Argument , das ist die neue Ressource. Es kann kein Löscher zusammen mit dieser neuen Ressource übergeben werden. Daher kann es in unserem Beispiel nicht mehr direkt mit geraden und ungeraden Zahlen verwendet werden. In der Tat der folgende Code:

IntUniquePtr p1(new int(42), deleteEvenNumber);
p1.reset(new int(43)); // can't pass deleteOddNumber

gibt natürlich aus:

Delete even number 42
Delete even number 43

was in unserem Fall falsch ist.

Tatsächlich könnten wir den Deleter in einer separaten Anweisung manuell ändern, indem wir die Tatsache ausnutzen, dass get_deleter Methode von unique_ptr gibt den Deleter durch nicht-konstanten Verweis zurück (danke an Marco Arena für den Hinweis):

p1.get_deleter() = deleteOddNumber;

Aber warum nicht reset Haben Sie ein Löschargument? Und wie man eine neue Ressource an einen std::unique_ptr übergibt zusammen mit dem entsprechenden Löscher in einer einzigen Anweisung?

Howard Hinnant, der unter anderem leitender Designer und Autor des std::unique_ptr ist Komponente, beantwortet diese Frage bei Stack Overflow:

Und so verwenden Sie seine Antwort in unserem ersten Beispiel:

IntUniquePtr p1(new int(42), deleteEvenNumber);
p1 = IntUniquePtr(new int(43), deleteOddNumber);

was die folgende gewünschte Ausgabe ergibt:

Delete even number 42
Delete odd number 43

Vielen Dank an Howard für die Bereitstellung dieser Lösung.

Seien Sie gespannt auf die nächste Folge der Serie Smart Developers Use Smart Pointers!

Verwandte Artikel:

  • Smart Pointer-Grundlagen
  • eindeutiger_ptr, gemeinsam genutzter_ptr, schwacher_ptr, bereichsbezogener_ptr, rohe Zeiger:Ihre Absichten klar darlegen, indem Sie Ihre intelligenten Zeiger kennen
  • Benutzerdefinierte Löscher und wie sie ausdrucksstärker werden
  • Wie man das Pimpl-Idiom mit Hilfe von unique_ptr implementiert
  • Wie man einen polymorphen Klon in modernem C++ erstellt
  • Wie man einen Smart Pointer zurückgibt UND Kovarianz verwendet (von Raoul Borges)