Funktionszeiger in Structs
Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 38 von 61.
Bisher haben wir Funktionszeiger als eigenständige Variablen verwendet oder sie als Argumente übergeben. Der nächste Schritt in Richtung Polymorphismus besteht darin, einen Funktionszeiger direkt in ein Struct einzubetten – was dem Struct sein eigenes Verhalten verleiht.
Wenn ein Struct einen Funktionszeiger enthält, kann jede Instanz eine andere Funktion enthalten. Das bedeutet, dass sich zwei Objekte desselben Typs unterschiedlich verhalten können, wenn Sie deren Funktionszeiger aufrufen:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Funktionszeiger als Member
} Printer;Die Printer-Struct hat nun zwei Member: Daten (name) und Verhalten (print). Um sie zu verwenden, weisen Sie dem Pointer während der Initialisierung eine Funktion zu:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Ausgabe: !!! Hello !!!
return 0;
}Beachten Sie, wie wir die Funktion über das Struct aufrufen: p.print("Hello"). Dies sieht bemerkenswert ähnlich aus wie der Aufruf einer Methode für ein Objekt in anderen Sprachen. Das Struct trägt sowohl seine Daten als auch die Funktion, die darauf operiert.
Dieses Muster ist die Grundlage für Polymorphismus in C – verschiedene Instanzen können unterschiedliche Funktionen zugewiesen haben, was zu unterschiedlichen Verhaltensweisen desselben struct-Typs führt.
Aufgabe
EinfachLassen Sie uns ein Notifier-System bauen, das demonstriert, wie Structs ihr eigenes Verhalten durch Funktionszeiger mitführen können.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
notifier.h: Definieren Sie einen Funktionszeigertyp namensNotifyFunc, der einenconst char*-Parameter entgegennimmt und nichts zurückgibt. Definieren Sie dann einNotifier-Struct, das einenname(einconst char*) und einennotify-Funktionszeiger vom TypNotifyFuncenthält.main.c: Binden Sie Ihren Header ein und implementieren Sie zwei Benachrichtigungsfunktionen:alert_notify— gibt die Nachricht im Format[ALERT] messageausinfo_notify— gibt die Nachricht im Format[INFO] messageaus
Notifier-Instanz, verknüpfen Sie die entsprechende Funktion basierend auf der Eingabe und rufen Sie die Benachrichtigung über den Funktionszeiger des Structs auf.
Ihr Programm erhält zwei Eingaben: einen Benachrichtigungstyp (1 für Alert, 2 für Info) und eine anzuzeigende Nachricht.
Weisen Sie basierend auf dem Typ die entsprechende Funktion dem notify-Member Ihres Notifiers zu und rufen Sie sie dann über das Struct auf, um die Nachricht anzuzeigen.
Beispielhafte Ausgabe, wenn der Typ 1 und die Nachricht "System starting" ist:
[ALERT] System startingBeispielhafte Ausgabe, wenn der Typ 2 und die Nachricht "All systems normal" ist:
[INFO] All systems normalDenken Sie daran, Include-Guards in Ihrer Header-Datei zu verwenden und die Funktion über das Struct-Member aufzurufen: n.notify(message)
Spickzettel
Funktionszeiger können direkt in Structs eingebettet werden, wodurch jede Instanz ihr eigenes Verhalten erhält. Dies ist eine Grundlage für Polymorphismus in C.
Definieren eines Structs mit einem Funktionszeiger
Definieren Sie zuerst einen Funktionszeigertyp und binden Sie ihn dann als Struct-Member ein:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Function pointer as a member
} Printer;Verwendung des Structs
Weisen Sie dem Zeiger während der Initialisierung eine Funktion zu und rufen Sie diese dann über den Struct auf:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Output: !!! Hello !!!
return 0;
}Die Syntax p.print("Hello") ruft die Funktion über den Struct auf, ähnlich wie der Aufruf einer Methode für ein Objekt in anderen Sprachen.
Polymorphes Verhalten
Verschiedene Instanzen desselben Struct-Typs können unterschiedliche zugewiesene Funktionen haben, was zu unterschiedlichen Verhaltensweisen desselben Struct-Typs führt.
Probier es selbst
#include <stdio.h>
#include "notifier.h"
// TODO: Implementiere die alert_notify Funktion
// Sollte ausgeben: [ALERT] message
// TODO: Implementiere die info_notify Funktion
// Sollte ausgeben: [INFO] message
int main() {
int type;
char message[256];
// Eingabe lesen
scanf("%d", &type);
getchar(); // Zeilenumbruch konsumieren
fgets(message, sizeof(message), stdin);
// Entferne abschließenden Zeilenumbruch von message, falls vorhanden
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Erstelle eine Notifier Instanz
// TODO: Basierend auf type (1 für alert, 2 für info),
// weise die entsprechende Funktion dem notify Member des notifiers zu
// TODO: Rufe die Benachrichtigung über den Funktionszeiger des structs auf
// Verwende: n.notify(message)
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
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-Implementierung