Was ist das Copy-and-Swap Idiom und warum es 2025 ein Must-have ist
Stellen Sie sich vor, Sie schreiben eine Klasse für die Arbeit mit dynamischem Speicher. Alles läuft gut, bis es an die Überladung des Zuweisungsoperators geht. Sie schreiben operator=, prüfen auf Selbstzuweisung, geben manuell alte Ressourcen frei, weisen neue zu … Und in 90 % der Fälle machen Sie einen Fehler, der zu einem Speicherleck oder doppeltem Löschen führt. Kommt Ihnen das bekannt vor?
In C++ gibt es eine elegante Lösung – das Copy-and-Swap Idiom. Es ist nicht nur ein modischer Trick, sondern eine grundlegende Technik, mit der Sie sicheren, ausnahmesicheren und prägnanten Code schreiben können. Im Jahr 2025, wo C++23 bereits aktiv genutzt wird und der Standard C++26 am Horizont steht, bleibt dieses Idiom der Goldstandard für Klassen, die Ressourcen verwalten.
In diesem Artikel analysieren wir das Copy-and-Swap Idiom von A bis Z: angefangen bei dem Problem, das es löst, bis hin zu praktischen Beispielen und Best Practices für 2025. Machen Sie Ihren Compiler bereit – es wird viel Code geben!
Das Problem: Warum ein naiver operator= böse ist
Beginnen wir mit einem klassischen Beispiel einer Klasse, die ein dynamisches Array verwaltet. Ohne das Copy-and-Swap Idiom sieht der Code oft so aus:
class DumbArray {public: DumbArray(size_t size) : size_(size), data_(new int[size]) {} ~DumbArray() { delete[] data_; } // Naiver Zuweisungsoperator DumbArray& operator=(const DumbArray& other) { if (this == &other) return *this; // Prüfung auf Selbstzuweisung delete[] data_; // Alte Ressourcen freigeben size_ = other.size_; data_ = new int[size_]; // Neue zuweisen std::copy(other.data_, other.data_ + size_, data_); return *this; } private: size_t size_; int* data_;};Auf den ersten Blick erscheint alles logisch. Aber es gibt drei Probleme:
- Fehlende grundlegende Ausnahmegarantie. Wenn
new int[size_]einstd::bad_allocauslöst, bleibt das Objekt in einem ungültigen Zustand:data_zeigt auf bereits gelöschten Speicher. - Doppelte Logik. Das Kopieren von Daten ist bereits im Kopierkonstruktor implementiert. Warum schreiben wir dasselbe manuell?
- Prüfung auf Selbstzuweisung. Sie verlangsamt den Code und wird leicht vergessen. Außerdem tritt in 95 % der Fälle keine Selbstzuweisung auf, die Prüfung wird trotzdem durchgeführt.
Und das ist noch nicht alles. Wenn Sie der Klasse weitere Ressourcen hinzufügen (z. B. Dateideskriptoren oder Mutexe), wird der Code nicht nur umständlich – er wird gefährlich. Genau hier kommt das Copy-and-Swap Idiom ins Spiel.
Das Wesen des Idioms: Drei Schritte zum perfekten operator=
Das Copy-and-Swap Idiom basiert auf einer einfachen Idee: Schreibe die Kopierlogik nicht im Zuweisungsoperator – verwende den Kopierkonstruktor und die swap-Funktion. Der Algorithmus sieht so aus:
- Copy: Erstelle eine temporäre Kopie des Objekts mit dem Kopierkonstruktor.
- Swap: Tausche den Inhalt des aktuellen Objekts mit der temporären Kopie aus.
- Destroy: Das temporäre Objekt wird zerstört und gibt die alten Ressourcen frei.
Im Code sieht das so aus:
class SmartArray {public: SmartArray(size_t size = 0) : size_(size), data_(size ? new int[size] : nullptr) {} // Kopierkonstruktor SmartArray(const SmartArray& other) : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) { std::copy(other.data_, other.data_ + size_, data_); } // Destruktor ~SmartArray() { delete[] data_; } // Friend-Funktion swap friend void swap(SmartArray& first, SmartArray& second) noexcept { using std::swap; swap(first.size_, second.siz