Menu
Coddy logo textTech

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 aus einem anderen Objekt desselben Typs initialisierst, ein Objekt per Wert übergibst oder ein Objekt per Wert zurückgibst.

Der Kopierkonstruktor nimmt eine konstante Referenz auf ein Objekt derselben Klasse entgegen:

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 aufgerufen
Player another(original);  // Ruft ebenfalls den Kopierkonstruktor auf

Wenn du keinen Kopierkonstruktor definierst, erzeugt der Compiler einen, der eine flache Kopie durchführt – er kopiert den Wert jedes Members direkt. Das funktioniert bei einfachen Typen problemlos, 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];
    }
    
    // Tiefkopie-Konstruktor
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Neuen Speicher allokieren
        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 hängenden Zeiger. Eine tiefe Kopie reserviert neuen Speicher und kopiert die tatsächlichen Daten, wodurch sichergestellt wird, dass jedes Objekt seine eigenen Ressourcen besitzt.

challenge icon

Aufgabe

Einfach

Erstelle ein Nachrichtenverlaufssystem, das die Bedeutung des tiefen Kopierens demonstriert, wenn deine Klasse dynamischen Speicher verwaltet. Du erstellst eine MessageLog-Klasse, die Nachrichten in einem dynamisch reservierten Array speichert, und implementierst einen geeigneten Kopierkonstruktor, damit jede Kopie ihren eigenen unabhängigen Speicher besitzt.

Du erstellst zwei Dateien, um deinen Code zu organisieren:

  • MessageLog.h: Definiere eine MessageLog-Klasse, die eine Sammlung von Nachrichten mithilfe dynamischen Speichers 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 eine count für die aktuelle Anzahl von Nachrichten
    • Einen parametrisierten Konstruktor, der eine capacity entgegennimmt, das Array auf dem Heap reserviert und count auf 0 initialisiert
    • Einen Kopierkonstruktor, der eine tiefe Kopie erstellt: Er reserviert neuen Speicher und kopiert alle Nachrichten aus dem Quellobjekt
    • Einen Destruktor, der den reservierten Speicher freigibt und "MessageLog destroyed" ausgibt
    • Eine addMessage(string msg)-Methode, die eine Nachricht hinzufügt, wenn noch Platz vorhanden ist
    • Eine getCount()-Methode, die die aktuelle Anzahl der Nachrichten zurückgibt
    • Eine getMessage(int index)-Methode, die die Nachricht am angegebenen Index zurückgibt
  • main.cpp: Zeige, dass dein Kopierkonstruktor eine unabhängige Kopie erstellt. Lies einen capacity-Wert und zwei Nachrichten aus der Eingabe ein und:
    • Erstelle ein MessageLog namens original mit der eingegebenen capacity
    • Füge beide Nachrichten zu original hinzu
    • Erstelle mithilfe des Kopierkonstruktors eine Kopie namens backup
    • Füge nur zu "New message" eine dritte Nachricht original hinzu
    • Gib "Original count: <count>" aus
    • Gib "Backup count: <count>" aus
    • Gib alle Nachrichten aus backup jeweils in einer eigenen Zeile aus

Das Eingabeformat lautet:

  • Erste Zeile: capacity (Ganzzahl)
  • Zweite Zeile: erste Nachricht (String)
  • Dritte Zeile: zweite Nachricht (String)

Wenn deine tiefe Kopie korrekt implementiert ist, sollte das Backup 2 Nachrichten enthalten, während das Original 3 Nachrichten enthält: Dies beweist, dass sie unabhängigen Speicher besitzen. Ohne einen geeigneten Kopierkonstruktor würden beide dasselbe Array verwenden, und Änderungen an einem Objekt 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 lesen
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

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

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

    // TODO: Erstelle eine Kopie namens 'backup' mit dem Copy-Konstruktor

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

    // TODO: Print "Original count: <count>"

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

    // TODO: Gib alle Nachrichten von 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