Wird geladen...

Konstruktoren und Destruktoren in C++

Lernen Sie Konstruktoren und Destruktoren in C++, um Objektlebenszyklen und Ressourcenverwaltung zu verstehen.

In C++ besitzt jede Klasse spezielle Methoden, die beim Erstellen und Zerstören eines Objekts aufgerufen werden: den Konstruktor und den Destruktor. Der Konstruktor bereitet das Objekt auf die erste Verwendung vor, während der Destruktor den Speicher bereinigt. Diese Struktur bildet die Grundlage des RAII (Resource Acquisition Is Initialization)-Prinzips.


1. Was ist ein Konstruktor?

Ein Konstruktor ist eine spezielle Methode, die automatisch aufgerufen wird, wenn ein Objekt einer Klasse erstellt wird. Sein Zweck besteht darin, Variablen zu initialisieren und notwendige Ressourcen vorzubereiten.


class Book {
private:
    string title;
    int pages;

public:
    // Standardkonstruktor
    Book() {
        title = "Unbekannt";
        pages = 0;
        cout << "Standardkonstruktor aufgerufen." << endl;
    }

    // Konstruktor mit Parametern
    Book(string t, int p) {
        title = t;
        pages = p;
        cout << "Parametrisierter Konstruktor aufgerufen." << endl;
    }

    void Display() {
        cout << "Buch: " << title << " (" << pages << " Seiten)" << endl;
    }
};

Der Konstruktor wird automatisch aufgerufen, wenn ein Objekt erstellt wird:


int main() {
    Book b1;
    Book b2("C++ Programmierung", 900);

    b1.Display();
    b2.Display();
}

2. Was ist ein Destruktor?

Ein Destruktor ist eine spezielle Methode, die aufgerufen wird, wenn die Lebensdauer eines Objekts endet. Er wird verwendet, um Aufräumarbeiten durchzuführen, wie das Freigeben von Speicher oder das Schließen von Dateien.


class Book {
public:
    Book() {
        cout << "Konstruktor ausgeführt." << endl;
    }

    ~Book() {
        cout << "Destruktor ausgeführt." << endl;
    }
};

int main() {
    Book b;
} // Am Ende von main() wird der Destruktor automatisch aufgerufen

Ausgabe:


Konstruktor ausgeführt.
Destruktor ausgeführt.

Da der Destruktor automatisch aufgerufen wird, sollte er nicht manuell aufgerufen werden.


3. Beispiel Book: Erweiterter Konstruktor und Destruktor

Machen wir nun die Klasse Book realistischer. Sie enthält dynamisch zugewiesene Daten (zum Beispiel eine Buchbeschreibung). So wird deutlich, warum Destruktoren wichtig sind.


#include <iostream>
#include <string>
using namespace std;

class Book {
private:
    string title;
    int pages;
    string *description; // Dynamischer Speicher

public:
    // Konstruktor
    Book(string t, int p, string d)
        : title(t), pages(p) {
        description = new string(d);
        cout << "Book-Konstruktor: " << title << endl;
    }

    // Destruktor
    ~Book() {
        cout << "Book-Destruktor: " << title << endl;
        delete description; // Dynamischer Speicher wird freigegeben
    }

    void Display() const {
        cout << "Buch: " << title << " (" << pages << " Seiten)" << endl;
        cout << "Beschreibung: " << *description << endl;
    }
};

int main() {
    Book b1("1984", 328, "Ein dystopischer Roman.");
    b1.Display();
}

Ausgabe:


Book-Konstruktor: 1984
Buch: 1984 (328 Seiten)
Beschreibung: Ein dystopischer Roman.
Book-Destruktor: 1984

Der Destruktor wird automatisch aufgerufen, wenn das Objekt am Ende von main() zerstört wird, und gibt den mit new reservierten Speicher wieder frei.


4. Konstruktor-Überladung

In derselben Klasse können mehrere Konstruktoren definiert werden. Der passende Konstruktor wird anhand der Anzahl und des Typs der Parameter aufgerufen.


class Book {
private:
    string title;
    int pages;

public:
    Book() : title("Unbekannt"), pages(0) {}
    Book(string t) : title(t), pages(100) {}
    Book(string t, int p) : title(t), pages(p) {}

    void Display() {
        cout << "Buch: " << title << " (" << pages << " Seiten)" << endl;
    }
};

int main() {
    Book b1;
    Book b2("C++ 101");
    Book b3("Design Patterns", 450);

    b1.Display();
    b2.Display();
    b3.Display();
}

5. Copy Constructor (Kopierkonstruktor)

Standardmäßig erstellt C++ automatisch einen Kopierkonstruktor, aber wenn dynamischer Speicher verwendet wird, sollte ein eigener Copy Constructor definiert werden.


class Book {
private:
    string title;
    string *desc;

public:
    Book(string t, string d) {
        title = t;
        desc = new string(d);
        cout << "Konstruktor: " << title << endl;
    }

    // Kopierkonstruktor
    Book(const Book &other) {
        title = other.title;
        desc = new string(*other.desc); // tiefe Kopie
        cout << "Copy Constructor: " << title << endl;
    }

    ~Book() {
        delete desc;
        cout << "Destruktor: " << title << endl;
    }

    void Display() {
        cout << title << " - " << *desc << endl;
    }
};

int main() {
    Book b1("C++ Primer", "Einsteigerhandbuch");
    Book b2 = b1; // Copy Constructor wird aufgerufen
    b2.Display();
}

Wenn keine tiefe Kopie (deep copy) durchgeführt wird, teilen sich beide Objekte denselben Speicherbereich, was zu einem „doppelten Delete“-Fehler führt, wenn der Destruktor ausgeführt wird.


6. Buchsystem – Lebenszyklusverfolgung

Das folgende Beispiel zeigt die Reihenfolge der Erstellung und Zerstörung eines Buchobjekts. Es ist ideal, um den tatsächlichen Lebenszyklus von Konstruktoren und Destruktoren zu verstehen.


#include <iostream>
#include <string>
using namespace std;

class Author {
public:
    Author(string n) { cout << "Autor erstellt: " << n << endl; }
    ~Author() { cout << "Autor zerstört." << endl; }
};

class Book {
private:
    string title;
    Author author;

public:
    Book(string t, string a) : title(t), author(a) {
        cout << "Buch erstellt: " << title << endl;
    }

    ~Book() {
        cout << "Buch zerstört: " << title << endl;
    }
};

int main() {
    Book b("Farm der Tiere", "George Orwell");
}

Ausgabe:


Autor erstellt: George Orwell
Buch erstellt: Farm der Tiere
Buch zerstört: Farm der Tiere
Autor zerstört.

Wie man sieht, wird zuerst der Author-Konstruktor aufgerufen, gefolgt vom Book-Konstruktor. Bei der Zerstörung geschieht das Gegenteil (zuerst Book, dann Author). Dies zeigt die Lebenszyklusreihenfolge bei Kompositionsbeziehungen in C++.


7. TL;DR

  • Konstruktor → Wird automatisch aufgerufen, wenn ein Objekt erstellt wird.
  • Destruktor → Wird automatisch aufgerufen, wenn der Lebenszyklus eines Objekts endet.
  • Konstruktoren können überladen werden, Destruktoren nicht.
  • Wenn dynamischer Speicher verwendet wird, muss der Destruktor delete aufrufen.
  • Der Copy Constructor sollte eine tiefe Kopie durchführen.
  • Erstellungsreihenfolge: zuerst Komponenten, dann die Klasse; Zerstörungsreihenfolge: zuerst die Klasse, dann die Komponenten.
  • Alle Beispiele können in Visual Studio 2022 oder GCC 11+ ausgeführt werden.

Ähnliche Artikel