Menu
CoddyTech

Kopierkonstruktor

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

Ein Kopierkonstruktor erstellt ein neues Objekt als Kopie eines vorhandenen Objekts. Er wird aufgerufen, wenn du ein Objekt mit einem anderen Objekt desselben Typs initialisierst, ein Objekt als Wert übergibst oder ein Objekt als Wert zurückgibst.

Der Kopierkonstruktor nimmt eine const-Referenz auf ein Objekt derselben Klasse entgegen. Es muss eine Referenz sein: Würde other als Wert übergeben, wäre dafür selbst eine Kopie erforderlich, wodurch der Kopierkonstruktor immer wieder und für immer aufgerufen würde. Sie ist const, weil das Erstellen einer Kopie das Original niemals verändern sollte:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Kopierkonstruktor
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Kopierkonstruktor wird aufgerufen
Player another(original);  // Ruft ebenfalls den Kopierkonstruktor auf

Wenn du keinen Kopierkonstruktor definierst, generiert der Compiler einen, der eine flache Kopie erstellt – er kopiert den Wert jedes Members direkt. Das funktioniert bei einfachen Typen gut, verursacht aber Probleme, wenn deine Klasse dynamischen Speicher verwaltet.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Kopierkonstruktor für eine tiefe Kopie
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Neuen Speicher zuweisen
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Inhalte kopieren
    }
    
    ~Buffer() { delete[] data; }
};

Ohne einen benutzerdefinierten Kopierkonstruktor würden beide Objekte auf denselben Speicher zeigen. Wenn eines zerstört wird, hätte das andere einen ungültigen Zeiger. Eine tiefe Kopie reserviert neuen Speicher und kopiert die tatsächlichen Daten, sodass jedes Objekt seine eigenen Ressourcen besitzt.

challenge icon

Aufgabe

Einfach

Erstelle ein Nachrichtenverlaufsystem, das die Bedeutung einer tiefen Kopie veranschaulicht, wenn deine Klasse dynamischen Speicher verwaltet. Du erstellst eine Klasse MessageLog, die Nachrichten in einem dynamisch zugewiesenen Array speichert, und implementierst einen geeigneten Kopierkonstruktor, damit jede Kopie ihren eigenen unabhängigen Speicherbereich hat.

Du erstellst zwei Dateien, um deinen Code zu organisieren:

  • MessageLog.h: Definiere eine Klasse MessageLog, die mithilfe von dynamischem Speicher eine Sammlung von Nachrichten verwaltet. Deine Klasse sollte Folgendes enthalten:
    • Private Member: einen Zeiger auf ein String-Array zum Speichern von Nachrichten, eine capacity für die maximale Anzahl von Nachrichten und einen count für die aktuelle Anzahl von Nachrichten
    • Einen parametrisierten Konstruktor, der eine Kapazität entgegennimmt, das Array auf dem Heap zuweist und count auf 0 initialisiert
    • Einen Kopierkonstruktor, der eine tiefe Kopie erstellt: Er weist neuen Speicher zu und kopiert alle Nachrichten aus dem Quellobjekt
    • Einen Destruktor, der den zugewiesenen Speicher freigibt und "MessageLog destroyed" ausgibt
    • Eine Methode addMessage(string msg), die eine Nachricht hinzufügt, wenn noch Platz ist
    • Eine Methode getCount(), die die aktuelle Anzahl der Nachrichten zurückgibt
    • Eine Methode getMessage(int index), die die Nachricht am angegebenen Index zurückgibt
  • main.cpp: Zeige, dass dein Kopierkonstruktor eine unabhängige Kopie erstellt. Lies einen Kapazitätswert und zwei Nachrichten aus der Eingabe ein und:
    • Erstelle ein MessageLog namens original mit der eingegebenen Kapazität
    • Füge beide Nachrichten zu original hinzu
    • Erstelle mithilfe des Kopierkonstruktors eine Kopie namens backup
    • Füge nur zu original eine dritte Nachricht "New message" hinzu
    • Gib "Original count: <count>" aus
    • Gib "Backup count: <count>" aus
    • Gib alle Nachrichten aus backup aus, eine pro Zeile

Das Eingabeformat lautet:

  • Erste Zeile: Kapazität (Ganzzahl)
  • Zweite Zeile: erste Nachricht (String)
  • Dritte Zeile: zweite Nachricht (String)

Wenn deine tiefe Kopie korrekt implementiert ist, sollte die Sicherung 2 Nachrichten haben, während das Original 3 hat: Das beweist, dass sie unabhängige Speicherbereiche besitzen. Ohne einen geeigneten Kopierkonstruktor würden beide dasselbe Array gemeinsam nutzen, und Änderungen an einem würden sich auf das andere auswirken!

Binde deine Header-Datei in main.cpp mit #include "MessageLog.h" ein.

Probier es selbst

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // Eingabe einlesen
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: Erstelle ein MessageLog namens 'original' mit der eingelesenen Kapazität

    // TODO: Füge beide Nachrichten zu original hinzu

    // TODO: Erstelle mit dem Kopierkonstruktor eine Kopie namens 'backup'

    // TODO: Füge "New message" nur zu original hinzu

    // TODO: Gib "Original count: <count>" aus

    // TODO: Gib "Backup count: <count>" aus

    // TODO: Gib alle Nachrichten aus backup aus, eine pro Zeile

    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

Übe selbstständig: Online-C++-Compiler