Menu
Coddy logo textTech

Rule of Three / Five / Zero

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 25 von 104.

Wenn Ihre Klasse Ressourcen wie dynamischen Speicher verwaltet, haben Sie gelernt, dass Sie einen benutzerdefinierten Destruktor, Kopierkonstruktor und Verschiebekonstruktor benötigen. Aber es gibt ein Leitprinzip, das Ihnen bei der Entscheidung hilft, welche speziellen Elementfunktionen implementiert werden sollen: die Regel der Drei, Fünf und Null.

Die Dreierregel (Rule of Three) besagt: Wenn Sie eine dieser drei definieren, sollten Sie alle drei definieren:

  • Destruktor
  • Kopierkonstruktor
  • Kopierzuweisungsoperator

Die Rule of Five erweitert dies für modernes C++ und fügt Verschiebeoperationen hinzu:

  • Destruktor
  • Kopierkonstruktor
  • Kopierzuweisungsoperator
  • Verschiebekonstruktor
  • Verschiebezuweisungsoperator
class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    ~Buffer() { delete[] data; }                          // 1. Destruktor
    Buffer(const Buffer& other);                          // 2. Kopierkonstruktor
    Buffer& operator=(const Buffer& other);               // 3. Kopierzuweisung
    Buffer(Buffer&& other) noexcept;                      // 4. Verschiebekonstruktor
    Buffer& operator=(Buffer&& other) noexcept;           // 5. Verschiebezuweisung
};

Die Rule of Zero ist der einfachste Ansatz: Wenn Ihre Klasse Ressourcen nicht direkt verwaltet, definieren Sie keine dieser Funktionen. Lassen Sie den Compiler diese generieren oder verwenden Sie Smart Pointer und Standard-Container, die Ressourcen für Sie verwalten.

class Player {
    std::string name;              // std::string verwaltet seinen eigenen Speicher
    std::vector<int> scores;       // std::vector verwaltet seine Ressourcen
public:
    Player(std::string n) : name(n) {}
    // Kein Destruktor, Kopier- oder Verschiebe-Funktionen benötigt!
};

Das Befolgen dieser Regeln verhindert Fehler wie Double-Deletion, Speicherlecks und Dangling Pointers, die auftreten, wenn einige Operationen definiert sind, andere jedoch fehlen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns eine TextBuffer-Klasse erstellen, die der Rule of Five folgt — indem wir alle fünf speziellen Elementfunktionen implementieren, um dynamisch zugewiesene Zeichendaten ordnungsgemäß zu verwalten. Dies wird zeigen, wie Kopier- und Verschiebeoperationen zusammenarbeiten, um eine robuste, ressourcenverwaltende Klasse zu erstellen.

Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:

  • TextBuffer.h: Definieren Sie eine TextBuffer-Klasse, die Text in einem dynamisch zugewiesenen Zeichen-Array speichert. Ihre Klasse benötigt:
    • Private Member: einen char*-Pointer namens data für den Textinhalt und eine size_t length für die Stringlänge (ohne Null-Terminator)
    • Einen parametrisierten Konstruktor, der einen C-String (const char*) entgegennimmt, Speicher zuweist, den Inhalt kopiert und "TextBuffer created: <text>" ausgibt
    • Einen Destruktor, der den Speicher freigibt (falls nicht null) und "TextBuffer destroyed" ausgibt
    • Einen Kopierkonstruktor, der eine tiefe Kopie (deep copy) durchführt und "TextBuffer copied" ausgibt
    • Einen Kopierzuweisungsoperator, der Selbstzuweisung behandelt, vorhandene Daten bereinigt, eine tiefe Kopie durchführt und "TextBuffer copy-assigned" ausgibt. Geben Sie *this zurück
    • Einen Verschiebekonstruktor (als noexcept markiert), der den Besitz überträgt und "TextBuffer moved" ausgibt. Hinterlassen Sie die Quelle in einem gültigen leeren Zustand
    • Einen Verschiebezuweisungsoperator (als noexcept markiert), der Selbstzuweisung behandelt, vorhandene Daten bereinigt, den Besitz überträgt und "TextBuffer move-assigned" ausgibt. Geben Sie *this zurück
    • Eine getText()-Methode, die den gespeicherten Text zurückgibt (geben Sie einen leeren String "" zurück, falls data null ist)
    • Eine getLength()-Methode, die die Länge zurückgibt
  • main.cpp: Demonstrieren Sie alle fünf speziellen Elementfunktionen in Aktion. Lesen Sie einen Text-String von der Eingabe ein, dann:
    • Erstellen Sie einen TextBuffer namens original mit dem eingegebenen Text
    • Erstellen Sie copied unter Verwendung des Kopierkonstruktors von original
    • Erstellen Sie another mit dem Text "Temporary"
    • Verwenden Sie die Kopierzuweisung: another = original
    • Erstellen Sie moved durch Verschiebekonstruktion von original unter Verwendung von std::move()
    • Erstellen Sie target mit dem Text "Target"
    • Verwenden Sie die Verschiebezuweisung: target = std::move(copied)
    • Geben Sie "--- Final State ---" aus
    • Geben Sie "original: <text> (length: <len>)" für jeden Buffer aus: original, copied, moved, another, target

Nach den Verschiebungen sollten die Quellobjekte (original und copied) leeren Text mit der Länge 0 anzeigen, während die Zielobjekte die übertragenen Daten halten. Dies demonstriert die Rule of Five in Aktion — alle fünf Funktionen arbeiten zusammen, um eine sichere Ressourcenverwaltung zu gewährleisten.

Binden Sie <cstring> für String-Funktionen wie strlen und strcpy ein, sowie <utility> für std::move().

Spickzettel

Die Rule of Three (Dreierregel) besagt, dass wenn Sie eine dieser drei speziellen Elementfunktionen definieren, Sie alle drei definieren sollten:

  • Destruktor
  • Kopierkonstruktor
  • Kopierzuweisungsoperator

Die Rule of Five (Fünferregel) erweitert dies für modernes C++, indem sie Verschiebeoperationen hinzufügt:

  • Destruktor
  • Kopierkonstruktor
  • Kopierzuweisungsoperator
  • Verschiebekonstruktor
  • Verschiebezuweisungsoperator
class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    ~Buffer() { delete[] data; }                          // 1. Destruktor
    Buffer(const Buffer& other);                          // 2. Kopierkonstruktor
    Buffer& operator=(const Buffer& other);               // 3. Kopierzuweisung
    Buffer(Buffer&& other) noexcept;                      // 4. Verschiebekonstruktor
    Buffer& operator=(Buffer&& other) noexcept;           // 5. Verschiebezuweisung
};

Die Rule of Zero (Nullerregel) besagt, dass wenn Ihre Klasse Ressourcen nicht direkt verwaltet, Sie keine speziellen Elementfunktionen definieren sollten. Verwenden Sie Smart Pointer und Standardcontainer, die Ressourcen automatisch verwalten:

class Player {
    std::string name;              // std::string verwaltet seinen eigenen Speicher
    std::vector<int> scores;       // std::vector verwaltet seine Ressourcen
public:
    Player(std::string n) : name(n) {}
    // Keine Destruktor-, Kopier- oder Verschiebe-Funktionen erforderlich!
};

Das Befolgen dieser Regeln verhindert Fehler wie doppeltes Freigeben (double-deletion), Speicherlecks und hängende Zeiger (dangling pointers).

Probier es selbst

#include <iostream>
#include <string>
#include <utility>
#include "TextBuffer.h"

using namespace std;

int main() {
    string input;
    getline(cin, input);

    // TODO: Erstelle einen TextBuffer namens 'original' mit dem eingegebenen Text

    // TODO: Erstelle 'copied' unter Verwendung des Kopierkonstruktors von 'original'

    // TODO: Erstelle 'another' mit dem Text "Temporary"

    // TODO: Verwende die Kopierzuweisung: another = original

    // TODO: Erstelle 'moved' durch Move-Konstruktion von 'original' unter Verwendung von std::move()

    // TODO: Erstelle 'target' mit dem Text "Target"

    // TODO: Verwende die Move-Zuweisung: target = std::move(copied)

    // TODO: Gib "--- Final State ---" aus

    // TODO: Gib den Zustand jedes Buffers in diesem Format aus:
    // "original: <text> (length: <len>)"
    // Ausgabe für: original, copied, moved, another, target

    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung