Opaque Structs definieren
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 18 von 61.
Nachdem du das Konzept der undurchsichtigen Zeiger verstanden hast, sehen wir uns nun an, wie man das Muster vervollständigt. Der Header deklariert, dass ein Typ existiert, aber in der Quelldatei definierst du, was tatsächlich darin enthalten ist.
Hier ist die vollständige Einrichtung. Zuerst der Header mit nur der Vorwärtsdeklaration:
// counter.h
#ifndef COUNTER_H
#define COUNTER_H
typedef struct Counter Counter; // Unvollständiger Typ
Counter *create_counter(int start);
void free_counter(Counter *c);
#endif
Dann enthält die Quelldatei die eigentliche Struct-Definition:
// counter.c
#include "counter.h"
#include <stdlib.h>
struct Counter {
int value; // Von außen verborgen!
int step;
};
Counter *create_counter(int start) {
Counter *c = malloc(sizeof(Counter));
c->value = start;
c->step = 1;
return c;
}
void free_counter(Counter *c) {
free(c);
}
Der entscheidende Punkt: struct Counter { ... } erscheint nur in der .c-Datei. Jede Datei, die counter.h einbindet, weiß, dass Counter existiert, kann aber value oder step nicht sehen. Der Versuch, c->value in main.c zu schreiben, würde einen Compilerfehler verursachen: Die Member sind tatsächlich unsichtbar.
Diese Trennung bildet die Grundlage der Kapselung in C. Der nächste Schritt besteht darin, kontrollierten Zugriff über Getter- und Setter-Funktionen bereitzustellen.
Aufgabe
EinfachLass uns ein Timer-Modul erstellen, das das Muster des opaken Zeigers demonstriert. Du übst, die Strukturdefinition innerhalb der Quelldatei zu verbergen, sodass ihre internen Member für jeglichen Code, der dein Modul verwendet, vollständig unsichtbar sind.
Du wirst drei Dateien erstellen:
timer.h: Dies ist deine öffentliche Schnittstelle. Deklariere einen opaken TypTimer, indem du nur eine Vorwärtsdeklaration verwendest: Hier darf kein Struct-Körper stehen! Deklariere außerdem zwei Funktionen:create_timer(nimmt eine Ganzzahl für die Sekunden entgegen und gibt einen Timer-Zeiger zurück) undfree_timer(nimmt einen Timer-Zeiger entgegen und räumt auf). Verwende Include-Guards mit dem SymbolTIMER_H.timer.c: Hier befindet sich das Geheimnis. Definiere die tatsächlichestruct Timermit zwei verborgenen Membern:int secondsundint running(verwende 1 für laufend und 0 für angehalten). Implementiere deinen Konstruktor so, dass er die Struktur allokiert,secondsmit dem bereitgestellten Wert initialisiert undrunningauf 1 setzt. Implementiere den Destruktor, um den Speicher freizugeben.main.c: Verwende dein Timer-Modul, um einen Timer zu erstellen, und bestätige, dass er erfolgreich erstellt wurde. Da die Struct-Member verborgen sind, kannst du nicht direkt auf sie zugreifen. Genau darum geht es! Gib einfach eine Meldung aus, die die Erstellung bestätigt, gib anschließend den Timer frei und gib eine Aufräummeldung aus.
Du erhältst eine Eingabe: die Anzahl der Sekunden (eine Ganzzahl), mit der der Timer initialisiert werden soll.
Erstelle in deiner Hauptdatei einen Timer mit dem bereitgestellten Sekundenwert. Gib eine Erstellungsmeldung aus, gib anschließend den Timer frei und gib eine Bestätigung aus.
Gib die Ausgabe in diesem Format aus:
Timer created
Timer freedDie entscheidende Erkenntnis ist, dass main.c mit Timer-Zeigern arbeiten kann, aber keine Möglichkeit hat, direkt auf seconds oder running zuzugreifen. Der Versuch, t->seconds in main.c zu schreiben, würde einen Compilerfehler verursachen, weil der Struct-Körper nur in timer.c definiert ist. Das ist echte Kapselung: Die Implementierungsdetails sind für die Benutzer deines Moduls vollständig verborgen.
Probier es selbst
#include <stdio.h>
#include "timer.h"
int main() {
int seconds;
scanf("%d", &seconds);
// TODO: Erstelle einen Timer mit dem angegebenen Sekundenwert
// TODO: Print "Timer created"
// TODO: Gib den Timer frei
// TODO: Gib "Timer freed" aus
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 Kontos3Objekt-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