Umsetzung der Move-Konstruktor durch den Aufruf Move-Zuweisungs-Operator
Dem MSDN-Artikel, Gewusst wie: Schreiben Sie einen Umzug Constuctor, hat die folgende Empfehlung.
Wenn Sie beide einen move-Konstruktor und move-Zuweisungs-operator für die Klasse, die Sie beseitigen können, um redundanten code schreiben
der move-Konstruktor-Aufruf der move-Zuweisungs-operator. Die
folgende Beispiel zeigt eine überarbeitete version des move-Konstruktor,
ruft der move-Zuweisungs-operator:
//Move constructor.
MemoryBlock(MemoryBlock&& other)
: _data(NULL)
, _length(0)
{
*this = std::move(other);
}
Ist dieser code ineffizient, indem Sie doppelt initialisieren MemoryBlock
's-Werte oder wird der compiler in der Lage sein, zu optimieren, entfernt die zusätzlichen Initialisierungen? Sollte ich immer schreiben, mein Umzug Konstruktoren durch den Aufruf der move-Zuweisungs-operator?
Du musst angemeldet sein, um einen Kommentar abzugeben.
In fast allen Fällen: ja.
Ja, nur die Umsetzung über move-Zuweisungs-operator, außer in den Fällen, in denen Sie gemessen, dass es führt zu suboptimaler Leistung.
Heutigen optimizer machen einen unglaublichen job bei der Optimierung von code. Dein Beispiel-code ist besonders einfach zu optimieren. Vorweg: die move-Konstruktor wird inline in fast allen Fällen. Wenn Sie die Umsetzung über move-assignment-operator, dann wird inline als auch.
Und schauen wir uns einige Montage! Diese zeigt den genauen code von der Microsoft-website ist mit beiden Versionen von der move-Konstruktor: manuell und über die move-Zuweisung. Hier ist die Montage-Ausgang für den GCC mit
-O
(-O1
hat den gleichen Ausgang; clang Ausgang führt zum gleichen Ergebnis):Abgesehen von den zusätzlichen Zweig für die richtige version, der code ist genau der gleiche. Bedeutung: doppelte Zuordnungen entfernt wurden.
Warum der zusätzliche Zweig? Der move-Zuweisungs-operator definiert durch die Microsoft-Seite nicht mehr Arbeit, als die move-Konstruktor: es ist geschützt gegen sich selbst-Zuordnung. Der move-Konstruktor ist nicht geschützt gegen die. Aber: wie ich bereits sagte, wird der Konstruktor wird inline in fast allen Fällen. Und in diesen Fällen, kann der Optimierer sehen, dass es nicht einem selbst Zuordnung, so dass dieser Zweig wird optimiert, heraus, zu.
Diese zu bekommen ist, viel wiederholt, aber es ist wichtig: nicht tun vorzeitige Mikro-Optimierung!
Versteh mich nicht falsch, auch ich hasse software, die Abfälle eine Menge von Ressourcen aufgrund der zu faul oder zu schlampig Entwickler oder management-Entscheidungen. Und Energiesparen ist nicht nur über Batterien, aber auch ein Umwelt-Thema, dem bin ich sehr begeistert von. Aber, tun Mikro-Optimierungen vorzeitig nicht helfen in dieser Hinsicht! Sicher, halten die Algorithmische Komplexität und die cache-Freundlichkeit Ihrer großen Daten in der Rückseite des Kopfes. Aber bevor Sie jede spezifische Optimierung, Messen!
In diesem speziellen Fall würde ich sogar vermuten, dass Sie nie haben, um die hand zu optimieren, da der compiler immer in der Lage sein, um optimalen code zu erzeugen um Ihre move-Konstruktor. Tun, die nutzlose Mikro-Optimierung jetzt kostet die Entwicklung Zeit später, wenn Sie Sie brauchen, um code an zwei Orten, oder wenn man debug ein merkwürdiger Fehler, der nur geschieht, weil Sie die geänderte code in nur einem Ort. Und das ist Verschwendete Entwicklungszeit, die könnte eher damit verbracht, nützliche Optimierungen.
Ich würde es nicht tun auf diese Weise. Der Grund für den Umzug die Mitglieder bestehen in Erster Linie ist Leistung. Tun Sie dies für Ihre move-Konstruktor wird wie Beschuss aus megabucks für ein super-Auto und dann versuchen, Geld zu sparen durch den Kauf von regulären gas.
Wenn Sie möchten, reduzieren die Menge an code, den Sie schreiben, nicht nur schreiben die Mitglieder verschieben. Ihre Klasse wird, kopieren Sie einfach schön, in einem Kontext bewegen.
Wenn Sie wollen, dass Ihr code, der zu einer hohen Leistung, dann passen Sie Ihre move-Konstruktor und move-Zuordnung zu sein, so schnell wie möglich. Guter Schachzug, die Mitglieder werden rasend schnell, und Sie sollten schätzen Ihre Geschwindigkeit, indem Sie zählen, lädt, speichert und Zweige. Wenn Sie etwas schreiben, mit 4 load/stores statt 8, - tun Sie es! Wenn Sie etwas schreiben, ohne äste statt 1, tun Sie es!
Wenn Sie (oder Ihre Kunden) stellen Sie Ihre Klasse in eine
std::vector
eine viel moves bekommen kann, erzeugt auf Ihre Art. Auch wenn Ihr Umzug blitzschnelle am 8 lädt/speichert, wenn man es doppelt so schnell, oder sogar 50% schneller mit nur 4 oder 6 lädt/speichert, imho, das ist gut investierte Zeit.Persönlich ich bin krank von das sehen warten-Cursor und bin bereit zu Spenden, ein extra 5 Minuten zu schreiben mein code und wissen, dass es so schnell wie möglich.
Wenn Sie noch nicht davon überzeugt, dass dies es Wert ist, schreiben Sie es beide Wege, und untersuchen Sie dann die generierte assembly mit voller Optimierung. Wer weiß, dein compiler ist wohl intelligent genug, um zu optimieren, entfernt zusätzliche Lasten und speichert für Sie. Aber zu dieser Zeit haben Sie bereits investiert mehr Zeit, als wenn Sie gerade geschrieben hatte, eine optimierte move-Konstruktor in den ersten Platz.
Mein C++11-version des
MemoryBlock
Klasse.Mit einer richtigen C++11 compiler
MemoryBlock::MemoryBlock(size_t)
sollte nur als 3-mal in der test-Programm.Ich denke nicht, wirst du bemerken, erhebliche performance-Unterschied. Ich halte es für eine gute Praxis, mit der move-Zuweisungs-operator aus der move-Konstruktor.
Allerdings würde ich eher verwenden, std::forward anstelle von std::move, weil es mehr logische:
std::forward
ist gedacht für die Verwendung in Vorlagen, wenn Sie nicht wissen, ob Sie können das Objekt verschieben oder nicht.Es hängt davon ab, was dein move-Zuweisungs-operator tut. Wenn man sich die one in dem Artikel, den Sie verlinkt sind, sehen Sie im Artikel:
So, in diesem Zusammenhang, wenn Sie rief die move-Zuweisungs-operator aus der move-Konstruktor ohne Initialisierung
_data
zuerst, Sie würde versuchen, am Ende löschen Sie einen nicht initialisierten Zeiger. So in diesem Beispiel, ineffizient oder nicht, es ist tatsächlich entscheidend, dass Sie initialisieren die Werte.:_data(other._data)
Ich würde einfach beseitigen Mitglied der Initialisierung und schreiben,
Das funktioniert immer, es sei denn der move-assignment exceptions werfen, und es in der Regel nicht!
Vorteile dieser Stile:
Ich denke, @Howard ' s post nicht ganz diese Frage beantworten. In der Praxis werden Klassen oft nicht, wie das kopieren, eine Menge von Klassen, deaktivieren Sie einfach copy-Konstruktor und copy-Zuweisung. Aber die meisten Klassen können beweglich sein, auch wenn Sie nicht kopierbar.