Menu
Coddy logo textTech

Überschreiben von Methoden

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

Methodenüberschreibung tritt auf, wenn eine abgeleitete Klasse ihre eigene Implementierung einer Methode bereitstellt, die bereits in der Basisklasse existiert. Dies ermöglicht es abgeleiteten Klassen, geerbtes Verhalten anzupassen, während die gleiche Methodensignatur beibehalten wird.

Um eine Methode zu überschreiben, definieren Sie einfach eine Methode in der abgeleiteten Klasse mit genau demselben Namen, Rückgabetyp und denselben Parametern:

class Animal {
public:
    void speak() {
        std::cout << "Some sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!" << std::endl;
    }
};

Dog d;
d.speak();  // Ausgabe: Woof!

Die Version der abgeleiteten Klasse verdeckt die Version der Basisklasse. Sie können jedoch weiterhin über den Bereichsauflösungsoperator auf die Methode der Basisklasse zugreifen:

class Dog : public Animal {
public:
    void speak() {
        Animal::speak();  // Version der Basisklasse aufrufen
        std::cout << "Woof!" << std::endl;
    }
};

Es gibt eine wichtige Einschränkung bei dieser grundlegenden Form des Überschreibens. Wenn Sie über einen Basisklassen-Zeiger oder eine Referenz auf ein abgeleitetes Objekt zugreifen, wird stattdessen die Version der Basisklasse aufgerufen:

Dog d;
Animal* ptr = &d;
ptr->speak();  // Ausgabe: Ein Geräusch (nicht Wuff!)

Dies geschieht, weil der Compiler basierend auf dem Zeigertyp bestimmt, welche Methode aufgerufen werden soll, und nicht basierend auf dem tatsächlichen Objekttyp. Um echte Laufzeit-Polymorphie zu erreichen, bei der die richtige Methode basierend auf dem tatsächlichen Objekt aufgerufen wird, benötigen Sie virtuelle Funktionen, die wir in der nächsten Lektion behandeln werden.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Benachrichtigungssystem erstellen, das das Überschreiben von Methoden (Method Overriding) in der Praxis demonstriert. Sie werden eine Basisklasse Notifier und zwei spezialisierte Benachrichtiger erstellen, die die Art und Weise der Nachrichtenzustellung anpassen.

Sie werden Ihren Code über drei Dateien organisieren:

  • Notifier.h: Definieren Sie eine Basisklasse Notifier mit:
    • Einem geschützten (protected) std::string recipient Member
    • Einem Konstruktor, der einen Empfängernamen entgegennimmt und speichert
    • Einer öffentlichen send(const std::string& message) Methode, die Folgendes ausgibt: Sending to <recipient>: <message>
  • EmailNotifier.h: Definieren Sie eine Klasse EmailNotifier, die öffentlich von Notifier erbt:
    • Einem Konstruktor, der einen Empfänger entgegennimmt und an die Basisklasse weitergibt
    • Überschreiben Sie die send() Methode, um zuerst die Version der Basisklasse mittels Notifier::send() aufzurufen und dann Folgendes auszugeben: [Email sent successfully]
  • main.cpp: Lesen Sie zwei Eingaben ein (jeweils in einer separaten Zeile):
    1. Empfängername (String)
    2. Nachrichteninhalt (String)

    Definieren Sie eine Klasse SMSNotifier direkt in der main.cpp, die öffentlich von Notifier erbt:

    • Einem Konstruktor, der einen Empfänger entgegennimmt und an die Basisklasse weitergibt
    • Überschreiben Sie die send() Methode, um Folgendes auszugeben: SMS to <recipient>: <message> (wobei das Basisverhalten vollständig ersetzt wird, ohne es aufzurufen)

    Erstellen Sie ein Objekt von jedem Benachrichtigungstyp (Basis Notifier, EmailNotifier und SMSNotifier) unter Verwendung desselben Empfängers. Rufen Sie send() für jedes Objekt mit der eingegebenen Nachricht auf und geben Sie zwischen jedem Aufruf eine Trennzeile aus:

    --- Base Notifier ---
    <output>
    --- Email Notifier ---
    <output>
    --- SMS Notifier ---
    <output>

Zum Beispiel mit den Eingaben Alice und Hello there!:

--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!

Beachten Sie, wie EmailNotifier das Basisverhalten erweitert, indem zuerst die Methode der Elternklasse aufgerufen wird, während SMSNotifier es vollständig durch eine eigene Implementierung ersetzt. Vergessen Sie nicht die Header-Guards und binden Sie die entsprechenden Header in jeder Datei ein.

Spickzettel

Method Overriding (Methoden-Überschreiben) ermöglicht es einer abgeleiteten Klasse, eine eigene Implementierung einer Methode der Basisklasse bereitzustellen, wobei die gleiche Methodensignatur (Name, Rückgabetyp und Parameter) beibehalten wird.

Grundlegende Syntax für das Überschreiben:

class Animal {
public:
    void speak() {
        std::cout << "Some sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!" << std::endl;
    }
};

Dog d;
d.speak();  // Output: Woof!

Die Methode der abgeleiteten Klasse verbirgt die Version der Basisklasse. Um die Methode der Basisklasse aus der abgeleiteten Klasse heraus aufzurufen, verwenden Sie den Bereichsauflösungs-Operator (Scope Resolution Operator):

class Dog : public Animal {
public:
    void speak() {
        Animal::speak();  // Call base class version
        std::cout << "Woof!" << std::endl;
    }
};

Wichtige Einschränkung: Wenn auf ein abgeleitetes Objekt über einen Zeiger oder eine Referenz der Basisklasse zugegriffen wird, wird die Version der Basisklasse aufgerufen:

Dog d;
Animal* ptr = &d;
ptr->speak();  // Output: Some sound (not Woof!)

Dies liegt daran, dass der Compiler basierend auf dem Zeigertyp bestimmt, welche Methode aufgerufen werden soll, und nicht basierend auf dem tatsächlichen Objekttyp. Für echte Laufzeit-Polymorphie verwenden Sie virtuelle Funktionen (virtual functions).

Probier es selbst

#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"

using namespace std;

// TODO: Definiere hier die Klasse SMSNotifier, die öffentlich von Notifier erbt
// - Konstruktor, der recipient an die Basisklasse übergibt
// - Überschreibe send(), um Folgendes auszugeben: SMS to <recipient>: <message>
//   (ersetze das Basisverhalten vollständig, rufe nicht die send() der Elternklasse auf)

int main() {
    string recipient;
    string message;
    
    getline(cin, recipient);
    getline(cin, message);
    
    // TODO: Erstelle ein Notifier-Objekt mit recipient
    // Gib "--- Base Notifier ---" aus
    // Rufe send() mit message auf
    
    // TODO: Erstelle ein EmailNotifier-Objekt mit recipient
    // Gib "--- Email Notifier ---" aus
    // Rufe send() mit message auf
    
    // TODO: Erstelle ein SMSNotifier-Objekt mit recipient
    // Gib "--- SMS Notifier ---" aus
    // Rufe send() mit message auf
    
    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