Zuweisungsoperator überladen
-
Don't Panik schrieb:
Du musst Copy&Swappen
Müssen nicht unbedingt. Es gibt Fälle, wo Copy&Swap nicht das beste ist, aber grundsätzlich kann man durchaus das Idiom nehmen und dann umsteigen, wenn der Bedarf da ist.
-
Danke und wie swappe ich das Objekt?
-
Overlord schrieb:
Danke und wie swappe ich das Objekt?
Memberweise. Das Array kannst du mit
std::swap_ranges()
elementweise tauschen. Oder du benutzt gleichstd::tr1::array
.Allerdings kann
swap()
bei Arrays keine Nothrow-Garantie mehr geben, zudem kommt es zu vielen Kopien. Daher verspricht der Copy-and-Swap-Vorschlag hier mehr, als er halten kann. Sollte dies ein Problem sein, kann ein dynamischer Container wiestd::vector
in Betracht gezogen werden. Wobei anzumerken ist, dass dieser auch einen gewissen Overhead mit sich bringt, der sich erst bei vielen Elementen (3 ist nichts) bezahlt macht.
-
Overlord schrieb:
Hallo, ich möchte gerne beim Überladen des Zuweisungsoperators den Kopiekonstruktor nutzen, [...]
Klasse::Klasse(const Klasse& rhs) { v_[0] = rhs.v_[0]; v_[1] = rhs.v_[1]; v_[2] = rhs.v_[2]; }
1. Kopierkonstruktor und Zuweisungsoperator unterscheiden sich schon konzeptionell voneinander. Der Kopierkonstruktor initialisiert ein neues Objekt, der Zuweisungsoperator verändert ein schon bestehendes Objekt. Man könnte aber den umgekehrten Weg gehen und den Kopierkonstruktor eine Default-Initialisierung gefolgt von einer Zuweisung machen lassen. Das würde ich aber in den seltensten Fällen empfehlen. Der Copy-&-Swap Trick ist zwar sehr sehr praktisch, aber was die Schreibarbeit angeht, wird diese natürlich nur vom Zuweisungsoperator zu einer swap-Funktion verlegt.
2. Dein Kopierkonstruktor sieht nicht so aus, als ob Du Kopierkonstruktor und Zuweisungsoperator überhaupt selbst definieren müsstest. Beachte, dass Kopierkonstruktor und Zuweisungsoperator sogenannte "special member functions" sind, die vom Compiler schon deklariert werden, wenn Du es nicht tust. Die Compiler-generierten Implementierungen kopieren bzw weisen elementweise zu. Das musst und solltest Du nicht selbst schreiben, wenn Du genau das Verhalten haben willst.
-
Ok danke, ich habe es jetzt einfach so gemacht. Es ist mir klar das hier die Defaultmethoden reichen würde, aber es diente hier nur zum Üben.
Klasse& Klasse::operator=(const Klasse& rhs) { if (this != &rhs) { v_[0] = rhs.v_[0]; v_[1] = rhs.v_[1]; v_[2] = rhs.v_[2]; } return *this; }
-
Welchen Sinn macht die this-Abfrage eigentlich? Um den Fall abzudecken, dass irgendein Benutzer sinnloserweise eine Selbstzuweisung macht, hat man eine Abfrage pro Zuweisung mehr, das ist doch nicht gut, oder?
-
Eisflamme schrieb:
Welchen Sinn macht die this-Abfrage eigentlich? Um den Fall abzudecken, dass irgendein Benutzer sinnloserweise eine Selbstzuweisung macht, hat man eine Abfrage pro Zuweisung mehr, das ist doch nicht gut, oder?
Man erwartet schon, dass ein benutzerdefinierter Typ, falls er die Zuweisung unterstützt, auch mit einer Selbstzuweisung klarkommt. Aber in diesem Fall ist die Abfrage eigentlich nicht nötig. Insofern hast Du Recht. Überhaupt ist es sinnfrei, einen solchen Oprator zu schreiben. (Siehe Begründung oben)
-
Eisflamme schrieb:
Welchen Sinn macht die this-Abfrage eigentlich? Um den Fall abzudecken, dass irgendein Benutzer sinnloserweise eine Selbstzuweisung macht, hat man eine Abfrage pro Zuweisung mehr, das ist doch nicht gut, oder?
In dem Fall natürlich unnötig, aber wie krümelkacker bereits erwähnte, tut es hier dir vom compiler generierten Funktionen auch.
Anders verhält es sich natürlich, wenn beispielsweise dynamisch Speicher alloziert wurde. Bei der Zuweisung würde man dann natürlich diesen erst freigeben und dann die Zuweisung vornehmen. Bei einer Selbstzuweisung hätte das aber zur Folge, dass danach die Werte nicht mehr vorhanden sind, da der rhs-Operand ja dem lhs-Operand entspricht, also auf beiden Seiten der Speicher freigegeben wurde. Daher normalerweise die vorherige Abfrage auf Selbstzuweisung.
-
Natürlich macht es auch Sinn bei so einem einfachen Beispiel wie hier. Der Inhalt von v_[] könnten ja auch weitere Objekte sein wie Biginteger oder sonst was. Nur weil es vielleicht in der ersten Implementierung nur ein einfacher Datentyp ist bedeutet dies nicht automatisch das es auf alle Ewigkeit zu bleibt.
-
Gimberley schrieb:
Natürlich macht es auch Sinn bei so einem einfachen Beispiel wie hier. Der Inhalt von v_[] könnten ja auch weitere Objekte sein wie Biginteger oder sonst was.
...welche selbst mit einer Selbstzuweisung klarkommen sollten. Daher braucht man diesen Test hier nicht. Und da eine Selbstzuweisung recht selten vorkommen sollte, würde ich diesen Test auch entfernen.
-
Falls man Copy-and-Swap sinnvoll anwenden kann, funktioniert Selbstzuweisung auch gerade. Und das ohne Spezialbehandlung.