In C++ wird beim Kopieren von Objekten zunächst der Mechanismus des memberwise copying verwendet: Für jedes Mitglied des Objekts wird die eigene Kopieroperation aufgerufen. Für eingebaute Typen ist das sicher, jedoch bei dynamischen Ressourcen entsteht ein Problem: Nur die Zeiger werden kopiert, nicht die tatsächlichen Daten.
Wenn ein Objekt einen Zeiger auf im Heap reservierten Speicher enthält, zeigen nach der Kopie von zwei Objekten beide auf denselben Speicherbereich. Wenn dann eines der Objekte zerstört wird, wird der Speicher freigegeben und der Zeiger des anderen wird ungültig ("wilder" Zeiger). Das führt zu Laufzeitfehlern und Speicherlecks.
Um sicherzustellen, dass die Kopie unabhängig ist, ist Deep Copy erforderlich – Byte-für-Byte-Kopie und Zuweisung eines eigenen Puffers. Dies wird durch Schreiben eines benutzerdefinierten Kopierkonstruktors und eines Zuweisungsoperators implementiert.
Beispielcode:
class MyString { char* data; public: MyString(const char* s) { data = new char[strlen(s)+1]; strcpy(data, s); } // Tiefen Kopierkonstruktor MyString(const MyString& src) { data = new char[strlen(src.data) + 1]; strcpy(data, src.data); } // Tiefen Kopierzuweisung MyString& operator=(const MyString& src) { if (this != &src) { delete[] data; data = new char[strlen(src.data) + 1]; strcpy(data, src.data); } return *this; } ~MyString() { delete[] data; } };
Wesentliche Merkmale:
Warum wird ein benutzerdefinierter Destruktor benötigt, wenn die Klasse nur einen Zeiger enthält, aber kein Speicher zugewiesen wird?
Ein Destruktor ist nur erforderlich, wenn Sie explizit Speicher (oder andere Ressourcen) innerhalb der Klasse zugewiesen haben. Wenn der Zeiger keinen Speicher zuweist, ist der Standarddestruktor ausreichend.
Was passiert, wenn operator= in einer Klasse mit dynamischem Speicher nicht implementiert wird, aber der Kopierkonstruktor deklariert ist?
Wenn Sie den Kopierkonstruktor manuell definiert haben, wird der Compiler operator= nicht automatisch implementieren; er wird entweder implizit deklariert oder der Compiler gibt einen Fehler/Warnung aus (hängt vom Standard ab). Dies führt zu einem schlecht definierten Verhalten bei der Zuweisung: Es erfolgt eine memberwise copy und es tritt entweder ein double free oder ein Leak auf.
Beispielcode:
MyString a("hi"); MyString b = a; // Ok: Ihr Kopierkonstruktor MyString c("bye"); c = a; // Problem! Wenn operator= nicht manuell implementiert wurde, erfolgt shallow copy
Warum ist es gefährlich, sich selbst bei der manuellen Implementierung von operator= zuzuweisen?
Wenn Ressourcen geteilt werden, ohne zu überprüfen, ob this != &rhs, wird bei der Zuweisung an sich selbst delete[] data aufgerufen, gefolgt von der Kopie eines bereits zerstörten Arrays, was zu einem segfault führt. Selbstschutz: Überprüfen Sie immer die Selbstzuweisung.
if (this != &rhs) { ... }
Ein Entwickler kopiert ein Objekt einer Klasse mit einem eingebauten Zeiger, ohne Deep Copy zu implementieren. Nach der Kopie teilen mehrere Objekte denselben Speicherbereich. Zwei Destruktoren führen zu einem double delete, die Anwendung stürzt ab.
Vorteile:
Nachteile:
Ein Entwickler implementiert den Kopierkonstruktor, den Zuweisungsoperator und den Destruktor richtig. Jedes Objekt verwaltet seinen eigenen Speicher.
Vorteile:
Nachteile: