Polymorphe Iteration
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 42 von 61.
Du hast gelernt, Schnittstellen zu erstellen und sie mit verschiedenen konkreten Funktionen zu implementieren. Der eigentliche Nutzen zeigt sich, wenn du eine Sammlung verschiedener Objekte einheitlich behandeln kannst: indem du sie durchläufst und für jedes denselben Funktionszeiger aufrufst, unabhängig davon, welches spezifische Verhalten damit verknüpft ist.
Stell dir vor, du hast ein Array von Action-Strukturen, denen jeweils eine eigene execute-Funktion zugewiesen ist:
typedef void (*ExecuteFunc)(void);
typedef struct {
const char* name;
ExecuteFunc execute;
} Action;
void jump(void) { printf("Jumping!\n"); }
void run(void) { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }Du kannst ein Array dieser Aktionen erstellen, wobei jede mit einer anderen Funktion verknüpft ist, und es dann mit einer einfachen for-Schleife durchlaufen:
int main() {
Action actions[3] = {
{ "Jump", jump },
{ "Run", run },
{ "Rest", rest }
};
for (int i = 0; i < 3; i++) {
printf("%s: ", actions[i].name);
actions[i].execute();
}
return 0;
}Die Schleife weiß nicht und kümmert sich nicht darum, was jede Aktion intern tut. Sie ruft einfach für jedes Element execute() auf.
Das ist Polymorphismus in Aktion: Derselbe Code verarbeitet Objekte mit völlig unterschiedlichen Verhaltensweisen. Das Hinzufügen eines neuen Aktionstyps erfordert keine Änderungen an der Iterationslogik; füge einfach ein weiteres Element zum array hinzu.
Aufgabe
EinfachErstellen wir ein Task-Verwaltungssystem, das polymorphe Iteration demonstriert: die Verarbeitung einer Sammlung von Objekten, die eine gemeinsame Schnittstelle verwenden, aber unterschiedliches Verhalten ausführen.
Du organisierst deinen Code über drei Dateien:
task.h: Definiere hier deine Task-Schnittstelle. Erstelle einen Funktionszeigertyp namensTaskFunc, der keine Parameter entgegennimmt und nichts zurückgibt. Definiere anschließend eineTask-Struktur mit einemname(einemconst char*) und einem Funktionszeigerrunvom TypTaskFunc.task.c: Implementiere drei verschiedene Task-Funktionen, die unterschiedliche Arten von Arbeit darstellen:backup_task: gibtBacking up data...auscleanup_task: gibtCleaning up files...ausreport_task: gibtGenerating report...aus
main.c: Führe hier alles zusammen. Erstelle ein Array ausTask-Strukturen, wobei jede mit einer anderen Task-Funktion verknüpft ist. Iteriere anschließend durch das Array und führe jede Task polymorph aus. Gib für jede Task ihren Namen aus und rufe danach ihrerun-Funktion auf.
Dein Programm erhält eine einzige Eingabe: die Anzahl der auszuführenden Tasks (1, 2 oder 3).
Erstelle ein Array, das genau drei Tasks in dieser Reihenfolge enthält: eine Backup-Task namens Backup, eine Cleanup-Task namens Cleanup und eine Report-Task namens Report. Iteriere abhängig von der Eingabeanzahl nur durch diese Anzahl von Tasks am Anfang des Arrays.
Gib für jede Task in deiner Iteration den Namen der Task gefolgt von einem Doppelpunkt und einem Leerzeichen aus und rufe anschließend ihre run-Funktion auf.
Beispielausgabe bei der Eingabe 2:
Backup: Backing up data...
Cleanup: Cleaning up files...Beispielausgabe bei der Eingabe 3:
Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...Die zentrale Erkenntnis ist, dass deine Schleife nicht wissen muss, was jede Task tut. Sie gibt einfach den Namen aus und ruft für jedes Element run() auf. Derselbe Iterationscode verarbeitet alle Task-Typen einheitlich. Denke daran, Include-Guards in deiner Headerdatei zu verwenden.
Probier es selbst
#include <stdio.h>
#include "task.h"
// Deklariere die in task.c implementierten Task-Funktionen
void backup_task(void);
void cleanup_task(void);
void report_task(void);
int main() {
int count;
scanf("%d", &count);
// TODO: Erstelle ein Array von 3 Task-Structs in dieser Reihenfolge:
// 1. Backup-Task namens "Backup" unter Verwendung von backup_task
// 2. Cleanup-Task namens "Cleanup" unter Verwendung von cleanup_task
// 3. Report-Task namens "Report" unter Verwendung von report_task
// TODO: Iteriere durch 'count' Tasks aus dem Array
// Für jeden Task ausgeben: "<name>: " und dann seine run-Funktion aufrufen
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der modularen Programmierung
Header-DateienInclude GuardsQuelldateienStatische FunktionenWiederholung: Modularer Taschenrechner4Kapselung
Konzept der Opaque PointersOpaque Structs definierenGetter und SetterValidierung in SetternRückblick: Die geheime Box2Objekte und Methoden
Structs als ObjekteDer 'Self'-PointerConst-CorrectnessPointer vs. WertHilfsmethodenZusammenfassung: Point Manager5Projekt: Einfaches Bankkonto
Projekt-SetupImplementierung des Kontos8Polymorphismus
Funktionszeiger in StructsMethoden simulierenDas Interface-KonzeptInterfaces implementierenPolymorphe IterationWiederholung: Greeter3Objekt-Lebenszyklus
Konstruktor-MusterDestruktor-MusterStack-InitialisierungTiefe KopieRückblick: String-Wrapper6Vererbung durch Komposition
Struct-EinbettungDie First-Member-RegelZugriff auf Parent-MemberUpcastingRückblick: Formenhierarchie9Projekt: Formen-Zeichner
ProjektübersichtKreis-ImplementierungRechteck-ImplementierungPolymorphe VerwendungShape-ContainerÜbe selbstständig: Online-C-Compiler