Die Drei-Regel in C++: Was jeder Entwickler wissen sollte

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Was ist die Drei-Regel in C++? Wir analysieren anhand von Beispielen



Wenn Sie jemals eine Klasse in C++ geschrieben haben, die dynamischen Speicher verwaltet (z. B. new oder malloc enthält), dann sind Sie sicherlich auf die Drei-Regel (Rule of Three) gestoßen. Dies ist eine der grundlegendsten Regeln der Sprache, die vor Speicherlecks, doppelten Löschungen und undefiniertem Verhalten schützt. Lassen Sie uns dies anhand realer Beispiele analysieren, damit Sie die Regel nicht nur auswendig lernen, sondern verstehen, warum sie funktioniert.



Stellen Sie sich vor: Sie haben eine Klasse geschrieben, die im Konstruktor Speicher allokiert. Der Compiler generiert freundlicherweise den Kopierkonstruktor und den Zuweisungsoperator für Sie. Aber er tut dies oberflächlich (shallow copy). Und dann beginnen die Probleme. Die Drei-Regel besagt: Wenn Ihre Klasse explizit einen Destruktor, Kopierkonstruktor oder Zuweisungsoperator definiert, dann müssen Sie höchstwahrscheinlich alle drei definieren. Warum? Weil dies ein Signal dafür ist, dass Ihre Klasse eine Ressource (Speicher, Datei, Netzwerkverbindung) verwaltet und die Kopie tief sein muss.



Problem: Flache Kopie (shallow copy)

Schauen wir uns ein typisches Beispiel an. Die Klasse String, die einen C-String im dynamischen Speicher speichert:



class String {private:    char* data;    size_t size;public:    // Konstruktor    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destruktor ~String() { delete[] data; }

// Kopierkonstruktor — vom Compiler generiert (flach) // Zuweisungsoperator — vom Compiler generiert (flach)

void print() const { std::cout << data << std::endl; }};


Alles sieht gut aus, bis wir versuchen, das Objekt zu kopieren:



int main() {    String s1("Hello");    String s2 = s1;  // Aufruf des Kopierkonstruktors (generiert)    s1.print();    s2.print();    // Das Programm stürzt mit einem double-free-Fehler ab!    return 0;}


Was ist passiert? Der Compiler hat einen Kopierkonstruktor generiert, der einfach den Zeiger data kopiert hat. Jetzt zeigen s1.data und s2.data auf denselben Speicherblock. Wenn die Objekte den Gültigkeitsbereich verlassen, werden ihre Destruktoren aufgerufen. Der erste Destruktor gibt den Speicher frei, der zweite versucht, ihn erneut freizugeben — double free. Das Programm stürzt ab oder verhält sich unvorhersehbar.



Lösung: Die Drei-Regel in Aktion

Um die Situation zu beheben, müssen alle drei speziellen Member-Funktionen implementiert werden. Das ist die Drei-Regel. Schreiben wir die korrekte Version der Klasse:



class String {private:    char* data;    size_t size;public:    // Konstruktor    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destruktor ~String() { delete[] data; }

// Kopierkonstruktor — tiefe Kopie String(const String& other) { size = other.size; data = new char[size + 1]; strcpy(data, other.data); }

// Zuweisungsoperator — tiefe Kopie String& operator=(const String& other) { if (this == &other) return *this; // Schutz vor Selbstzuweisung

delete[] data; // Alte Ressourcen freigeben

size = other.size; data = new char[size + 1]; strcpy(data, other.data);

return *this; }

void print() const { std::cout << data << std::endl; }};


Jetzt funktioniert das Kopieren korrekt: Jedes Objekt besitzt seinen eigenen Speicherblock. Auch die Zuweisung funktioniert ohne

Blogs

Buchempfehlungen