Frage

Da ein Copykonstruktor

MyClass(const MyClass&);

und ein Operator = Überlastung

MyClass& operator = (const MyClass&);

haben so ziemlich den gleichen Code, den gleichen Parameter und unterscheiden sich nur bei der Rückkehr ist es möglich, dass sie eine gemeinsame Funktion haben beide zu benutzen?

War es hilfreich?

Lösung

Ja. Es gibt zwei gängige Möglichkeiten. Ein - in der Regel abgeraten - ist die operator= vom Copykonstruktor zu nennen explizit:

MyClass(const MyClass& other)
{
    operator=(other);
}

Um jedoch eine gute operator= bietet, ist eine Herausforderung, wenn es darum geht, mit dem alten Zustand zu tun haben und Fragen, die sich aus der Selbstzuweisung. Auch alle Mitglieder und Basen erhalten standardmäßig initialisiert zuerst selbst wenn sie von other werden zugewiesen sind. Dies kann nicht einmal gültig für alle Mitglieder und Basen und auch dort, wo es gilt, es ist semantisch überflüssig und kann praktisch teuer sein.

Eine zunehmend beliebte Lösung ist operator= mit dem Copy-Konstruktor und eine Swap-Methode zu implementieren.

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}

oder auch:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

Eine swap Funktion ist in der Regel einfach zu schreiben, wie es nur das Eigentum der Einbauten Swaps und keine bestehenden Zustand zu bereinigen oder zuteilen neuen Ressourcen.

Die Vorteile der Kopie und Swap-Idioms ist, dass es automatisch Selbstzuordnung sicher ist und - vorausgesetzt, dass der Swap-Betrieb nicht Wurf -. Ist auch stark Ausnahme sicher

Um stark Ausnahme sicher zu sein, eine ‚Hand‘ geschrieben Zuweisungsoperator hat in der Regel eine Kopie der neuen Ressourcen zuzuweisen, bevor de-Zuweisung der alten Ressourcen des Anmelders, so dass, wenn eine Ausnahme der neuen Ressourcen erfolgt die Zuteilung, der alte Zustand kann noch werden zurückgegeben. All dies kommt kostenlos mit copy-and-Swap ist jedoch typischerweise komplexer und damit fehleranfällig, von Grund auf zu tun.

Das einzige, was vorsichtig zu sein, von sicher zu machen ist, dass die Swap-Methode eine echte Swap ist, und nicht der Standard std::swap die den Kopierkonstruktor und Zuweisungsoperator verwendet selbst.

Typischerweise wird ein element swap verwendet. std::swap arbeitet und ‚no-throw‘ garantiert mit allen Grundarten und Zeigertypen. Die meist Smart-Pointer kann auch mit einer nicht-throw Garantie ausgetauscht werden.

Andere Tipps

Der Copykonstruktor führt erstmalige Initialisierung von Objekten, die verwendeten Ausgang Erinnerung. Der Zuweisungsoperator, OTOH, überschreibt vorhandene Werte durch neue. Öfter als nie, beinhaltet diese alte Ressourcen Abweisung (zB Speicher) und neue Zuteilung.

Wenn es eine Ähnlichkeit zwischen den beiden, dann ist es, dass der Zuweisungsoperator führt eine Zerstörung und Copy-Konstruktion. Einige Entwickler verwendet, um tatsächlich Zuordnung zu implementieren, indem Sie an Ort und Stelle Zerstörung gefolgt von Platzierung copy-Konstruktion. Dies ist jedoch eine sehr schlechte Idee. (Was passiert, wenn dies ist der Zuweisungsoperator einer Basisklasse, dass bei der Zuordnung einer abgeleiteten Klasse aufgerufen?)

Was in der Regel ist das kanonische Idiom heute gilt mit swap als Charles vorgeschlagen:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

Diese Anwendungen kopieren Konstruktion (beachten Sie, dass other kopiert wird) und Zerstörung (es am Ende der Funktion zerstört wird) - und nutzt sie in der richtigen Reihenfolge zu: Bau vor Zerstörung (vielleicht gescheitert) (muss nicht Scheitern).

Etwas stört mich über:

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}

Als erstes liest das Wort „swap“ wenn mein Geist denkt „kopieren“ ärgert meinen gesunden Menschenverstand. Auch frage ich mich, das Ziel dieser Phantasie Trick. Ja, alle Ausnahmen, die neuen (kopiert) Ressourcen bei der Konstruktion sollte geschehen, bevor der Swap, die wie eine sichere Art und Weise scheint alles, um sicherzustellen, werden die neuen Daten, bevor sie es live geschaltet gefüllt ist.

Das ist in Ordnung. Also, was über Ausnahmen, die nach dem Austausch geschehen? (Wenn die alten Ressourcen zerstört werden, wenn das temporäre Objekt des Anwendungsbereich geht aus) Aus der Sicht des Benutzers von der Abtretung hat der Vorgang fehlgeschlagen ist, außer es nicht. Es hat einen riesigen Nebeneffekt: die Kopie hat tatsächlich passieren. Es war nur einige Ressource Bereinigung dass fehlgeschlagen. Der Zustand des Zielobjekts verändert wurde, obwohl der Betrieb scheint von außen nicht bestanden.

Also, ich schlage statt "swap" einen natürlichere "Transfer" zu tun:

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    transfer(tmp);
    return *this;
}

Es ist immer noch der Bau des temporären Objekt, aber die nächste sofortige Maßnahmen ist es, alle aktuellen Ressourcen des Ziels zu befreien, bevor er (und nulling damit sie nicht doppelt befreit sein) die Ressourcen der Quelle zu.

Statt {Konstrukt, zu verschieben, zu zerstören}, schlage ich vor, {Konstrukt, zerstört, zu verschieben}. Der Umzug, der die gefährlichste Aktion ist, die eine letzte genommen, nachdem alles andere erledigt ist.

Ja, Zerstörung fail ist ein Problem in jedem Schema. Die Daten werden entweder beschädigt (kopiert, wenn Sie nicht denken, es ist) oder verloren (freigegeben, wenn Sie nicht denken, es ist). Verloren ist besser als beschädigt. Keine Daten besser als schlechte Daten.

Transfer statt Swap. Das ist mein Vorschlag sowieso.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top