Da ein Kopierkonstruktor
MyClass(const MyClass&);
und eine = Bedienerüberlastung
MyClass& operator = (const MyClass&);
Haben Sie so ziemlich den gleichen Code, den gleichen Parameter und unterscheiden Sie sich nur bei der Rückgabe. Ist es möglich, eine gemeinsame Funktion für beide zu haben?
c++
variable-assignment
copy-constructor
c++-faq
MPelletier
quelle
quelle
Antworten:
Ja. Es gibt zwei gängige Optionen. Eine - von der im Allgemeinen abgeraten wird - besteht darin, den
operator=
vom Kopierkonstruktor explizit aufzurufen :Die Bereitstellung eines Gutes
operator=
ist jedoch eine Herausforderung, wenn es darum geht, mit dem alten Zustand und Problemen umzugehen, die sich aus der Selbstzuweisung ergeben. Außerdem werden alle Mitglieder und Basen zuerst standardmäßig initialisiert, auch wenn sie von zugewiesen werden sollenother
. Dies gilt möglicherweise nicht für alle Mitglieder und Basen, und selbst wenn es gültig ist, ist es semantisch redundant und kann praktisch teuer sein.Eine zunehmend beliebte Lösung ist die Implementierung
operator=
mit dem Kopierkonstruktor und einer Swap-Methode.oder auch:
Eine
swap
Funktion ist normalerweise einfach zu schreiben, da sie nur den Besitz der Interna austauscht und nicht den vorhandenen Status bereinigen oder neue Ressourcen zuweisen muss.Der Vorteil der Kopier- und Swap-Sprache besteht darin, dass sie automatisch selbstzuweisungssicher ist und - vorausgesetzt, der Swap-Vorgang ist kein Wurf - auch stark ausnahmesicher ist.
Um eine starke Ausnahmesicherheit zu gewährleisten, muss ein handgeschriebener Zuweisungsoperator normalerweise eine Kopie der neuen Ressourcen zuweisen, bevor die Zuweisung der alten Ressourcen des Empfängers aufgehoben wird, damit der alte Status weiterhin wiederhergestellt werden kann, wenn eine Ausnahme beim Zuweisen der neuen Ressourcen auftritt . All dies ist mit Copy-and-Swap kostenlos, ist jedoch in der Regel komplexer und daher fehleranfällig und kann von Grund auf neu ausgeführt werden.
Das eine, worauf Sie achten müssen, ist sicherzustellen, dass die Swap-Methode ein echter Swap ist und nicht die Standardeinstellung, bei
std::swap
der der Kopierkonstruktor und der Zuweisungsoperator selbst verwendet werden.In der Regel wird ein Mitglied
swap
verwendet.std::swap
funktioniert und ist bei allen Grundtypen und Zeigertypen garantiert. Die meisten intelligenten Zeiger können auch mit einer No-Throw-Garantie ausgetauscht werden.quelle
operator=
aus dem Kopiercode in der Tat ziemlich schlecht, da zunächst alle Werte auf einen Standardwert initialisiert werden, um sie unmittelbar danach mit den Werten des anderen Objekts zu überschreiben.assign
Mitgliedsfunktion zu haben, die in einigen Fällen sowohl vom Kopierer als auch vom Zuweisungsoperator verwendet wird (für Lightweight-Klassen). In anderen Fällen (ressourcenintensiv / Fälle verwenden, Handle / Body) ist ein Kopieren / Tauschen natürlich der richtige Weg.Der Kopierkonstruktor führt die erstmalige Initialisierung von Objekten durch, die früher Rohspeicher waren. Der Zuweisungsoperator OTOH überschreibt vorhandene Werte durch neue. Meistens werden dabei alte Ressourcen (z. B. Speicher) verworfen und neue zugewiesen.
Wenn es eine Ähnlichkeit zwischen den beiden gibt, führt der Zuweisungsoperator die Zerstörung und die Kopierkonstruktion durch. Einige Entwickler implementierten die Zuweisung tatsächlich durch direkte Zerstörung, gefolgt von der Erstellung von Platzierungskopien. Dies ist jedoch eine sehr schlechte Idee. (Was ist, wenn dies der Zuweisungsoperator einer Basisklasse ist, der während der Zuweisung einer abgeleiteten Klasse aufgerufen wurde?)
Was heutzutage normalerweise als kanonische Redewendung angesehen wird, ist die Verwendung,
swap
wie Charles vorgeschlagen hat:Dies verwendet Kopierkonstruktion (Anmerkung,
other
die kopiert wird) und Zerstörung (sie wird am Ende der Funktion zerstört) - und sie verwendet sie auch in der richtigen Reihenfolge: Konstruktion (könnte fehlschlagen) vor Zerstörung (darf nicht fehlschlagen).quelle
swap
deklariert werdenvirtual
?Etwas stört mich an:
Erstens irritiert das Lesen des Wortes "Tauschen", wenn mein Verstand "Kopieren" denkt, meinen gesunden Menschenverstand. Außerdem stelle ich das Ziel dieses ausgefallenen Tricks in Frage. Ja, Ausnahmen beim Erstellen der neuen (kopierten) Ressourcen sollten vor dem Austausch auftreten. Dies scheint ein sicherer Weg zu sein, um sicherzustellen, dass alle neuen Daten gefüllt sind, bevor sie live geschaltet werden.
Das ist gut. Was ist also mit Ausnahmen, die nach dem Tausch auftreten? (Wenn die alten Ressourcen zerstört werden, wenn das temporäre Objekt den Gültigkeitsbereich verlässt.) Aus Sicht des Benutzers der Zuweisung ist die Operation fehlgeschlagen, außer dies war nicht der Fall. Es hat einen großen Nebeneffekt: Die Kopie ist tatsächlich passiert. Es war nur eine Ressourcenbereinigung, die fehlgeschlagen ist. Der Status des Zielobjekts wurde geändert, obwohl der Vorgang von außen fehlgeschlagen zu sein scheint.
Also schlage ich vor, anstelle von "Swap" einen natürlicheren "Transfer" durchzuführen:
Es gibt noch die Konstruktion des temporären Objekts, aber die nächste sofortige Aktion besteht darin, alle aktuellen Ressourcen des Ziels freizugeben, bevor die Ressourcen der Quelle dorthin verschoben werden (und NULL, damit sie nicht doppelt freigegeben werden).
Anstelle von {konstruieren, bewegen, zerstören} schlage ich {konstruieren, zerstören, bewegen} vor. Der Zug, der die gefährlichste Aktion ist, ist der letzte, der ausgeführt wird, nachdem alles andere erledigt ist.
Ja, ein Fehlschlag der Zerstörung ist in beiden Schemata ein Problem. Die Daten sind entweder beschädigt (kopiert, wenn Sie nicht dachten, dass dies der Fall ist) oder verloren (freigegeben, wenn Sie nicht glauben, dass dies der Fall ist). Verloren ist besser als korrupt. Keine Daten sind besser als schlechte Daten.
Übertragen statt tauschen. Das ist sowieso mein Vorschlag.
quelle
First, reading the word "swap" when my mind is thinking "copy" irritates
-> Als Bibliotheksschreiber kennen Sie normalerweise gängige Praktiken (Kopieren + Tauschen), und der springende Punkt istmy mind
. Ihr Geist ist tatsächlich hinter der öffentlichen Schnittstelle verborgen. Darum geht es bei wiederverwendbarem Code.