Iterator-Muster
Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 56 von 61.
Das Iterator-Muster bietet eine Möglichkeit, nacheinander auf die Elemente einer Sammlung zuzugreifen, ohne deren zugrunde liegende Struktur offenzulegen. Anstatt den Benutzern direkten Zugriff auf ein Array zu geben, geben Sie ihnen ein Objekt, das weiß, wie man die Daten Element für Element durchläuft.
Ein Iterator benötigt normalerweise zwei Informationen: eine Referenz auf die Sammlung und die aktuelle Position. In C erstellen wir ein struct, um diesen Zustand zu speichern:
typedef struct {
int* data; // Zeiger auf das Array
int size; // Gesamtzahl der Elemente
int current; // aktuelle Position
} IntIterator;Der Iterator stellt zwei wichtige Funktionen bereit. has_next() prüft, ob weitere Elemente zum Lesen vorhanden sind, und next() gibt das aktuelle Element zurück und rückt die Position vor:
int has_next(IntIterator* it) {
return it->current < it->size;
}
int next(IntIterator* it) {
return it->data[it->current++];
}Die Verwendung des Iterators sieht sauber aus und verbirgt die Details des Arrays:
IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
printf("%d\n", next(&it));
}Der Aufrufer muss nie wissen, dass numbers ein Array ist oder wie die Indizierung funktioniert. Diese Abstraktion macht es einfach, die zugrunde liegende Datenstruktur später zu ändern – zum Beispiel von einem Array zu einer verketteten Liste –, ohne den Code zu ändern, der den Iterator verwendet.
Aufgabe
EinfachLassen Sie uns einen NumberList-Iterator bauen — eine saubere Abstraktion, die es Ihnen ermöglicht, eine Sammlung von Ganzzahlen zu durchlaufen, ohne die zugrunde liegende Array-Struktur offenzulegen.
Sie werden Ihren Code in drei Dateien organisieren:
iterator.h: Definieren Sie einIntIterator-Struct, das einen Zeiger auf ein Integer-Array, die Gesamtgröße der Sammlung und die aktuelle Position enthält. Deklarieren Sie drei Funktionen:create_iterator(nimmt einen Array-Zeiger und die Größe entgegen, gibt einen initialisierten Iterator zurück),has_next(prüft, ob weitere Elemente vorhanden sind) undnext(gibt das aktuelle Element zurück und rückt die Position vor). Vergessen Sie die Include-Guards nicht!iterator.c: Implementieren Sie Ihre Iterator-Funktionen. Die Funktioncreate_iteratorsollte einenIntIteratorals Wert zurückgeben, wobei die aktuelle Position auf 0 gesetzt ist. Die Funktionhas_nextgibt 1 zurück, wenn noch mehr Elemente zu lesen sind, andernfalls 0. Die Funktionnextgibt das Element an der aktuellen Position zurück und erhöht dann die Position.main.c: Lesen Sie die Anzahl der Elemente ein und lesen Sie dann jeden Integer-Wert in ein Array ein. Erstellen Sie einen Iterator für dieses Array und verwenden Sie dann eine while-Schleife mithas_nextundnext, um jedes Element zu durchlaufen und in einer eigenen Zeile auszugeben.
Ihr Programm erhält:
- Die Anzahl der Elemente im Array
- Jeden Integer-Wert in einer separaten Zeile
Verwenden Sie das Iterator-Muster, um alle Elemente auszugeben — keine direkte Array-Indizierung in Ihrer Traversierungsschleife!
Beispielhafte Ausgabe, wenn die Eingaben 4, dann 10, 20, 30, 40 sind:
10
20
30
40Beispielhafte Ausgabe, wenn die Eingaben 3, dann -5, 0, 100 sind:
-5
0
100Beispielhafte Ausgabe, wenn die Eingaben 1, dann 42 sind:
42Der Iterator verbirgt, wie die Daten gespeichert sind — Ihre Hauptschleife fragt einfach "gibt es mehr?" und "gib mir das nächste", ohne etwas über Array-Indizes oder das Speicherlayout zu wissen.
Spickzettel
Das Iterator-Muster bietet eine Möglichkeit, sequenziell auf Elemente einer Sammlung zuzugreifen, ohne deren zugrunde liegende Struktur offenzulegen.
Ein Iterator-Struct enthält die Referenz auf die Sammlung, die Größe und die aktuelle Position:
typedef struct {
int* data; // Zeiger auf das Array
int size; // Gesamtzahl der Elemente
int current; // aktuelle Position
} IntIterator;Der Iterator stellt zwei Schlüsselfunktionen bereit:
int has_next(IntIterator* it) {
return it->current < it->size;
}
int next(IntIterator* it) {
return it->data[it->current++];
}Die Verwendung des Iterators verbirgt die Implementierungsdetails des Arrays:
IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
printf("%d\n", next(&it));
}Diese Abstraktion ermöglicht es, die zugrunde liegende Datenstruktur zu ändern, ohne den Client-Code modifizieren zu müssen.
Probier es selbst
#include <stdio.h>
#include "iterator.h"
int main() {
int n;
scanf("%d", &n);
int arr[n];
// TODO: Lies n ganzzahlige Werte in das Array ein
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// TODO: Erstelle einen Iterator für das Array
// TODO: Verwende eine while-Schleife mit has_next und next zum Durchlaufen
// und gib jedes Element in einer eigenen Zeile aus
// Verwende KEINE direkte Array-Indizierung in deiner Durchlaufschleife!
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 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-Implementierung