Menu
Coddy logo textTech

Zuweisungsoperatorüberladung

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

Der Kopierzuweisungsoperator (=) wird aufgerufen, wenn Sie ein bestehendes Objekt einem anderen zuweisen. Im Gegensatz zum Kopierkonstruktor (der ein neues Objekt erstellt), arbeitet der Zuweisungsoperator mit einem Objekt, das bereits existiert und möglicherweise Ressourcen hat, die zuerst bereinigt werden müssen.

class Buffer {
    int* data;
    size_t size;
    
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    ~Buffer() { delete[] data; }
    
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {              // 1. Prüfung auf Selbstzuweisung
            delete[] data;                  // 2. Bestehende Ressource bereinigen
            size = other.size;              // 3. Daten kopieren
            data = new int[size];
            for (size_t i = 0; i < size; ++i)
                data[i] = other.data[i];
        }
        return *this;                       // 4. *this zurückgeben
    }
};

Die Prüfung auf Selbstzuweisung (this != &other) ist entscheidend. Ohne sie würde b = b; die Daten löschen, bevor versucht wird, sie zu kopieren, was zu undefiniertem Verhalten führt.

Der Operator gibt *this per Referenz zurück, um Verkettungen wie a = b = c; zu ermöglichen. Dies entspricht dem Verhalten eingebauter Typen.

Für Klassen, die Ressourcen verwalten, werden Sie oft sowohl Kopier- als auch Verschiebe-Zuweisungsoperatoren implementieren:

Buffer& operator=(Buffer&& other) noexcept {
    if (this != &other) {
        delete[] data;           // Aktuelle Ressource bereinigen
        data = other.data;       // Ressource übernehmen
        size = other.size;
        other.data = nullptr;    // Quelle in gültigem Zustand hinterlassen
        other.size = 0;
    }
    return *this;
}

Der Move-Zuweisungsoperator überträgt den Besitz, anstatt zu kopieren, wodurch Operationen wie vec[0] = createBuffer(); viel effizienter werden.

challenge icon

Aufgabe

Einfach

Lassen Sie uns eine DynamicString-Klasse erstellen, die ein dynamisch zugewiesenes Zeichenarray verwaltet und Zuweisungen zwischen bestehenden Objekten ordnungsgemäß handhabt. Dies ist ein praktisches Szenario, in dem Sie String-Daten sicher von einem Objekt auf ein anderes übertragen müssen, ohne Speicherlecks oder hängende Zeiger zu verursachen.

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

  • DynamicString.h: Definieren Sie eine DynamicString-Klasse, die einen C-Style-String unter Verwendung von dynamischem Speicher speichert. Ihre Klasse sollte Folgendes verwalten:
    • Einen privaten char*-Zeiger für die String-Daten
    • Ein privates size_t für die Länge
    • Einen Konstruktor, der einen const char* entgegennimmt und eine tiefe Kopie erstellt
    • Einen Destruktor, der den zugewiesenen Speicher ordnungsgemäß freigibt
    • Einen Kopierzuweisungsoperator, der eine tiefe Kopie mit Schutz vor Selbstzuweisung durchführt
    • Einen Verschiebezuweisungsoperator, der den Besitz effizient überträgt
    • Eine c_str()-Methode, die den internen String zurückgibt (const)
    • Eine length()-Methode, die die Stringlänge zurückgibt (const)

    Ihr Kopierzuweisungsoperator muss die vier wesentlichen Schritte befolgen: Prüfung auf Selbstzuweisung, Bereinigung vorhandener Ressourcen, Kopieren der neuen Daten und Rückgabe von *this. Der Verschiebezuweisungsoperator sollte den Zeiger der Quelle übernehmen ("stehlen") und die Quelle in einem gültigen leeren Zustand hinterlassen (nullptr mit Länge 0).

  • main.cpp: Lesen Sie drei Strings von der Eingabe ein (jeder in einer separaten Zeile). Erstellen Sie drei DynamicString-Objekte: str1 mit der ersten Eingabe, str2 mit der zweiten Eingabe und str3 mit der dritten Eingabe.

    Demonstrieren Sie Ihre Zuweisungsoperatoren:

    1. Geben Sie den Anfangszustand aller drei Strings aus
    2. Verwenden Sie die Kopierzuweisung: str1 = str2;
    3. Geben Sie str1 und str2 nach der Kopierzuweisung aus (beide sollten den gleichen Inhalt haben)
    4. Verwenden Sie die Verschiebezuweisung: str2 = std::move(str3);
    5. Geben Sie str2 nach der Verschiebezuweisung und die Länge von str3 aus (sollte 0 sein, nachdem verschoben wurde)

    Ausgabeformat:

    Initial:
    str1: <value>
    str2: <value>
    str3: <value>
    After copy (str1 = str2):
    str1: <value>
    str2: <value>
    After move (str2 = std::move(str3)):
    str2: <value>
    str3 length: 0

Verwenden Sie <cstring> für strlen und strcpy. Markieren Sie Ihren Verschiebezuweisungsoperator als noexcept. Binden Sie <utility> in main.cpp für std::move ein. Vergessen Sie nicht die Header-Guards in Ihrer Header-Datei.

Spickzettel

Der Kopierzuweisungsoperator (=) weist ein bestehendes Objekt einem anderen zu. Er muss die Bereinigung bestehender Ressourcen verarbeiten, bevor neue Daten kopiert werden.

Struktur des Kopierzuweisungsoperators

ClassName& operator=(const ClassName& other) {
    if (this != &other) {              // 1. Self-assignment check
        // 2. Clean up existing resources
        // 3. Copy data from other
    }
    return *this;                       // 4. Return *this
}

Beispiel mit dynamischem Speicher

class Buffer {
    int* data;
    size_t size;
    
public:
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            delete[] data;                  // Clean up existing resource
            size = other.size;
            data = new int[size];
            for (size_t i = 0; i < size; ++i)
                data[i] = other.data[i];
        }
        return *this;
    }
};

Die Prüfung auf Selbstzuweisung (this != &other) verhindert das Löschen von Daten vor dem Kopieren in Fällen wie b = b;.

Die Rückgabe von *this als Referenz ermöglicht Verkettungen: a = b = c;.

Verschiebezuweisungsoperator

Der Verschiebezuweisungsoperator überträgt den Besitz, anstatt zu kopieren, was die Effizienz verbessert:

ClassName& operator=(ClassName&& other) noexcept {
    if (this != &other) {
        // Clean up current resource
        // Steal the resource from other
        // Leave other in valid state (nullptr, 0, etc.)
    }
    return *this;
}

Beispiel Verschiebezuweisung

Buffer& operator=(Buffer&& other) noexcept {
    if (this != &other) {
        delete[] data;
        data = other.data;       // Steal the resource
        size = other.size;
        other.data = nullptr;    // Leave source valid
        other.size = 0;
    }
    return *this;
}

Markieren Sie die Verschiebezuweisung als noexcept für eine optimale Performance mit Standard-Containern.

Probier es selbst

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

using namespace std;

int main() {
    // Lies drei Strings von der Eingabe ein
    string input1, input2, input3;
    getline(cin, input1);
    getline(cin, input2);
    getline(cin, input3);

    // Erstelle drei DynamicString-Objekte
    DynamicString str1(input1.c_str());
    DynamicString str2(input2.c_str());
    DynamicString str3(input3.c_str());

    // TODO: Gib den Anfangszustand aller drei Strings aus
    // Format:
    // Initial:
    // str1: <value>
    // str2: <value>
    // str3: <value>

    // TODO: Verwende die Kopierzuweisung: str1 = str2;

    // TODO: Gib str1 und str2 nach der Kopierzuweisung aus
    // Format:
    // After copy (str1 = str2):
    // str1: <value>
    // str2: <value>

    // TODO: Verwende die Verschiebezuweisung: str2 = std::move(str3);

    // TODO: Gib str2 nach dem Verschieben und die Länge von str3 aus
    // Format:
    // After move (str2 = std::move(str3)):
    // str2: <value>
    // str3 length: 0

    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