web-dev-qa-db-ger.com

Kopierkonstruktor und = Operatorüberladung in C ++: Ist eine gemeinsame Funktion möglich?

Da ein Kopierkonstruktor

MyClass(const MyClass&);

und an = 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 verwenden?

80
MPelletier

Ja. Es gibt zwei gängige Optionen. Eine - von der allgemein abgeraten wird - ist, die operator= aus dem Kopierkonstruktor explizit:

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

Wenn Sie jedoch ein gutes operator= ist eine Herausforderung, wenn es um den Umgang mit dem alten Staat und Fragen geht, die sich aus der Selbstzuweisung ergeben. Außerdem werden alle Mitglieder und Basen standardmäßig zuerst initialisiert, auch wenn sie von other zugewiesen werden sollen. 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 von operator= mit dem Kopierkonstruktor und einer Swap-Methode.

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, da sie nur den Besitz der Interna austauscht und nicht den vorhandenen Status bereinigen oder neue Ressourcen zuweisen muss.

Der Vorteil der Kopier- und Auslagerungssprache besteht darin, dass sie automatisch selbst zuweisungssicher ist und - vorausgesetzt, die Auslagerungsoperation ist nicht ausschlaggebend - auch ausnahmesicher ist.

Um eine hohe Ausnahmesicherheit zu gewährleisten, muss ein von Hand geschriebener Zuweisungsoperator in der Regel eine Kopie der neuen Ressourcen zuweisen, bevor die Zuordnung der alten Ressourcen des Empfängers aufgehoben wird. Wenn also eine Ausnahme bei der Zuweisung der neuen Ressourcen auftritt, kann der alte Zustand weiterhin wiederhergestellt werden . All dies ist mit Copy-and-Swap kostenlos, ist jedoch in der Regel komplexer und daher fehleranfällig.

Achten Sie darauf, dass es sich bei der Swap-Methode um eine echte Swap-Methode handelt und nicht um die Standardmethode std::swap, das den Kopierkonstruktor und den Zuweisungsoperator selbst verwendet.

In der Regel wird ein memberwise swap verwendet. std::swap funktioniert und ist bei allen Grundtypen und Zeigertypen 'no-throw' garantiert. Die meisten intelligenten Zeiger können auch mit einer No-Throw-Garantie ausgetauscht werden.

111
CB Bailey

Der Kopierkonstruktor führt die Erstinitialisierung von Objekten durch, bei denen es sich früher um Raw Memory handelte. Der Zuweisungsoperator OTOH überschreibt vorhandene Werte mit neuen. Meist geht es darum, alte Ressourcen (z. B. Speicher) zu entlassen und neue zuzuweisen.

Wenn es eine Ähnlichkeit zwischen den beiden gibt, führt der Zuweisungsoperator die Zerstörung und den Kopieraufbau durch. Einige Entwickler implementierten die Zuweisung tatsächlich durch In-Place-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 von swap, wie Charles es vorschlug:

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

Dies verwendet Kopierkonstruktion (beachte, dass other kopiert wird) und Zerstörung (es wird am Ende der Funktion zerstört) - und es verwendet sie auch in der richtigen Reihenfolge: Konstruktion (kann fehlschlagen) vor Zerstörung ( darf nicht scheitern).

12
sbi