In der C++-Programmiersprache Edition 4 gibt es ein Beispiel einer Vektorimplementierung, siehe relevanten Code am Ende der Nachricht.
uninitialized_move() initialisiert neue T-Objekte in den neuen Speicherbereich, indem sie aus dem alten Speicherbereich verschoben werden. Dann ruft es den Destruktor für das ursprüngliche T-Objekt auf, das Objekt, aus dem es verschoben wurde. Warum ist der Destruktor-Aufruf in diesem Fall notwendig?
Hier ist mein unvollständiges Verständnis: Das Verschieben eines Objekts bedeutet, dass das Eigentum an den Ressourcen, die dem verschobenen Objekt gehören, auf das verschobene Objekt übertragen wird. Die Reste im verschobenen Objekt sind einige mögliche Mitglieder von eingebauten Typen, die nicht zerstört werden müssen, sie werden freigegeben, wenn die vector_base b den Gültigkeitsbereich verlässt (innerhalb Reservieren()nach dem Tauschen() Anruf). Alle Zeiger im verschobenen Objekt müssen auf nullptr gesetzt werden, oder es wird ein Mechanismus verwendet, um den Besitz des verschobenen Objekts auf diesen Ressourcen zu löschen, damit wir sicher sind, warum dann den Destruktor für ein erschöpftes Objekt aufrufen, wenn die “vector_base b ” Der Destruktor wird den Speicher sowieso freigeben, nachdem der Austausch abgeschlossen ist?
Ich verstehe die Notwendigkeit, den Destruktor in den Fällen explizit aufzurufen, in denen er aufgerufen werden muss, weil wir etwas zu zerstören haben (z. B. Elemente löschen), aber ich sehe seine Bedeutung nach std::move + deallocation von vector_base nicht. Ich habe einige Texte im Netz gelesen und sehe den Destruktoraufruf des verschobenen Objekts als Signal (an wen oder was?), dass die Lebensdauer des Objekts abgelaufen ist.
Bitte klären Sie mich auf, welche sinnvolle Arbeit der Destruktor noch erledigen muss. Vielen Dank!
Das folgende Code-Snippet stammt von hier http://www.stroustrup.com/4th_printing3.html
template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return; // never decrease allocation
vector_base<T,A> b {vb.alloc,size(),newalloc-size()}; // get new space
uninitialized_move(vb.elem,vb.elem+size(),b.elem); // move elements
swap(vb,b); // install new base
} // implicitly release old space
template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
using T = Value_type<Out>; // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_)
for (; b!=e; ++b,++oo) {
new(static_cast<void*>(&*oo)) T{move(*b)}; // move construct
b->~T(); // destroy
}
return oo;
}
Dies ist eine generische Implementierung, Sie wissen es jetzt nicht einmal
T{move(*b)}
ruft einen Bewegungskonstruktor auf.– dyp
14. Dezember 2013 um 22:38 Uhr
Denken Sie nicht an einen Umzug zu viel. Bewegen ist in erster Näherung wie Kopieren, und die Welt spielt immer noch nach denselben Regeln.
– Kerrek SB
14. Dezember 2013 um 22:45 Uhr
🙂 aber verschieben ist sehr wichtig, gerade weil es nicht kopieren ist, was in manchen Fällen zu teuer ist, also muss ich es anders als kopieren betrachten. Beim Kopieren kann ich entscheiden, ob ich das Original fallen lassen möchte oder nicht, beim Bewegen sage ich sozusagen: “Ich bin jetzt der neue Besitzer Ihrer Sachen und Sie sind erschöpft.”
– cvomake
14. Dezember 2013 um 22:53 Uhr