Menu
Coddy logo textTech

Virtuelle Funktionen erneut betrachtet

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

Nachdem wir nun den Unterschied zwischen Polymorphismus zur Compile-Zeit und zur Laufzeit verstanden haben, werfen wir einen genaueren Blick auf virtuelle Funktionen und das Schlüsselwort override, das ihre Verwendung sicherer macht.

Wenn du eine Funktion in einer Basisklasse als virtual kennzeichnest, können abgeleitete Klassen ihre eigene Implementierung bereitstellen. Der Spezifizierer override teilt dem Compiler ausdrücklich mit, dass du beabsichtigst, eine virtuelle Funktion zu überschreiben:

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

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

Die Verwendung von override ist entscheidend, da dadurch Fehler bereits zur Kompilierzeit erkannt werden. Wenn du den Funktionsnamen versehentlich falsch schreibst oder die falschen Parameter verwendest, wird der Compiler dich darauf hinweisen, anstatt stillschweigend eine neue Funktion zu erstellen:

class Cat : public Animal {
public:
    void speek() override {  // Compilerfehler: keine Funktion zum Überschreiben
        std::cout << "Meow!" << std::endl;
    }
};

Der Spezifizierer final verhindert weiteres Überschreiben. Verwende ihn, wenn eine abgeleitete Klasse die letzte sein soll, die eine bestimmte Funktion überschreibt:

class Bulldog : public Dog {
public:
    void speak() override final {
        std::cout << "Gruff woof!" << std::endl;
    }
};

class TinyBulldog : public Bulldog {
    void speak() override {}  // Fehler: finale Funktion kann nicht überschrieben werden
};

Verwende beim Überschreiben virtueller Funktionen immer override. Dadurch dokumentierst du deine Absicht und ermöglichst es dem Compiler zu überprüfen, ob du tatsächlich eine vorhandene virtuelle Funktion überschreibst.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Benachrichtigungssystem, das die Leistungsfähigkeit der Schlüsselwörter override und final mit virtuellen Funktionen demonstriert. Du erstellst eine Hierarchie von Benachrichtigungs-Handlern, in der einige Methoden weiter angepasst werden können, während andere gesperrt sind, um Änderungen zu verhindern.

Du organisierst deinen Code auf drei Dateien:

  • Notifier.h: Definiere eine Basisklasse Notifier, die einen beliebigen Absender von Benachrichtigungen darstellt:
    • Ein geschütztes Element std::string recipient
    • Einen Konstruktor, der den Namen des Empfängers entgegennimmt und speichert
    • Eine virtuelle Methode send(const std::string& message), die Folgendes ausgibt: Notifying <recipient>: <message>
    • Eine virtuelle Methode getType(), die den String "Generic" zurückgibt
    • Einen virtuellen Destruktor
  • EmailNotifier.h: Definiere eine Klasse EmailNotifier, die von Notifier erbt:
    • Ein privates Element std::string domain
    • Einen Konstruktor, der Empfänger und Domain entgegennimmt und den Empfänger an die Basisklasse übergibt
    • Überschreibe send(), sodass Folgendes ausgegeben wird: Emailing <recipient>@<domain>: <message>
    • Überschreibe getType() und markiere die Methode mit final. Sie soll "Email" zurückgeben

    Definiere anschließend eine Klasse UrgentEmailNotifier, die von EmailNotifier erbt:

    • Einen Konstruktor, der Empfänger und Domain entgegennimmt und beide an EmailNotifier übergibt
    • Überschreibe send(), sodass Folgendes ausgegeben wird: [URGENT] Emailing <recipient>@<domain>: <message>
    • Hinweis: Du kannst getType() hier nicht überschreiben, da die Methode in final mit EmailNotifier markiert wurde
  • main.cpp: Lies drei Eingaben ein (jeweils in einer eigenen Zeile):
    1. Name des Empfängers
    2. E-Mail-Domain
    3. Nachrichtentext

    Erstelle dynamisch drei Benachrichtigungsobjekte: ein Notifier, ein EmailNotifier und ein UrgentEmailNotifier, alle mit demselben Empfänger (und, falls zutreffend, derselben Domain). Speichere sie in einem Array aus Notifier*-Zeigern.

    Durchlaufe das Array und gib für jeden Benachrichtigungsdienst seinen Typ mithilfe von getType() aus. Rufe anschließend send() mit deiner Nachricht auf. Formatiere jeden Eintrag wie folgt:

    Type: <type>
    <send output>

    Gib zwischen den einzelnen Benachrichtigungsdiensten eine Leerzeile aus. Räume die dynamisch angelegten Objekte anschließend auf.

Beispielsweise mit den Eingaben Alice, company.com und Meeting at 3pm:

Type: Generic
Notifying Alice: Meeting at 3pm

Type: Email
Emailing Alice@company.com: Meeting at 3pm

Type: Email
[URGENT] Emailing Alice@company.com: Meeting at 3pm

Beachte, wie UrgentEmailNotifier send() überschreiben kann, um das Nachrichtenformat anzupassen, aber den Typ "Email" von EmailNotifier erbt, da getType() mit final markiert wurde. Verwende das Schlüsselwort override für alle überschriebenen Methoden, um Signaturabweichungen bereits zur Kompilierzeit zu erkennen.

Probier es selbst

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

using namespace std;

int main() {
    // Eingaben lesen
    string recipient;
    string domain;
    string message;
    
    getline(cin, recipient);
    getline(cin, domain);
    getline(cin, message);
    
    // TODO: Erstelle ein Array von Notifier*-Zeigern mit 3 Elementen
    
    // TODO: Dynamisch erstellen:
    // - Einen Basis-Notifier mit dem recipient
    // - Einen EmailNotifier mit recipient und domain
    // - Einen UrgentEmailNotifier mit recipient und domain
    
    // TODO: Durchlaufe das Array und für jeden Notifier:
    // - Gib "Type: " gefolgt vom Ergebnis von getType() aus
    // - Rufe send() mit der message auf
    // - Gib eine Leerzeile zwischen den Notifiern aus (nicht nach dem letzten)
    
    // TODO: Räume dynamisch allokierte Objekte 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

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