Menu
Coddy logo textTech

Opaque Structs definieren

Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 18 von 61.

Nachdem Sie nun das Konzept der opaken Pointer verstanden haben, schauen wir uns an, wie man das Muster vervollständigt. Der Header deklariert, dass ein Typ existiert, aber die Quelldatei ist der Ort, an dem Sie definieren, was sich tatsächlich darin befindet.

Hier ist der vollständige Aufbau. 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

Anschließend enthält die Quelldatei die tatsächliche 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 Compiler-Fehler verursachen – die Member sind wirklich unsichtbar.

Diese Trennung ist die Grundlage der Kapselung in C. Der nächste Schritt besteht darin, einen kontrollierten Zugriff über Getter- und Setter-Funktionen bereitzustellen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Timer-Modul erstellen, das das Opaque-Pointer-Muster demonstriert. Sie werden üben, die Struct-Definition innerhalb der Quelldatei zu verbergen, sodass ihre internen Mitglieder für jeglichen Code, der Ihr Modul verwendet, völlig unsichtbar sind.

Sie werden drei Dateien erstellen:

  • timer.h: Dies ist Ihre öffentliche Schnittstelle. Deklarieren Sie einen opaken Timer-Typ nur mit einer Vorwärtsdeklaration – kein Struct-Body hier! Deklarieren Sie außerdem zwei Funktionen: create_timer (nimmt eine Ganzzahl für Sekunden entgegen und gibt einen Timer-Pointer zurück) und free_timer (nimmt einen Timer-Pointer entgegen und räumt auf). Verwenden Sie Include-Guards mit dem Symbol TIMER_H.
  • timer.c: Hier lebt das Geheimnis. Definieren Sie die tatsächliche struct Timer mit zwei verborgenen Mitgliedern: int seconds und int running (verwenden Sie 1 für laufend, 0 für gestoppt). Implementieren Sie Ihren Konstruktor, um das Struct zu allozieren, seconds mit dem bereitgestellten Wert zu initialisieren und running auf 1 zu setzen. Implementieren Sie den Destruktor, um den Speicher freizugeben.
  • main.c: Verwenden Sie Ihr Timer-Modul, um einen Timer zu erstellen und zu bestätigen, dass er erfolgreich erstellt wurde. Da die Struct-Mitglieder verborgen sind, können Sie nicht direkt auf sie zugreifen – das ist der ganze Punkt! Geben Sie einfach eine Nachricht aus, die die Erstellung bestätigt, geben Sie dann den Timer frei und geben Sie eine Bereinigungsnachricht aus.

Sie erhalten eine Eingabe: die Anzahl der Sekunden (eine Ganzzahl), mit der der Timer initialisiert werden soll.

Erstellen Sie in Ihrer Hauptdatei einen Timer mit dem angegebenen Sekundenwert. Geben Sie eine Erstellungsnachricht aus, geben Sie dann den Timer frei und geben Sie eine Bestätigung aus.

Geben Sie die Ausgabe in diesem Format aus:

Timer created
Timer freed

Die entscheidende Erkenntnis hierbei ist, dass main.c mit Timer-Pointern arbeiten kann, aber keine Möglichkeit hat, direkt auf seconds oder running zuzugreifen. Der Versuch, t->seconds in main.c zu schreiben, würde zu einem Compilerfehler führen, da der Struct-Body nur in timer.c definiert ist. Dies ist echte Kapselung – die Implementierungsdetails sind für die Benutzer Ihres Moduls vollständig verborgen.

Spickzettel

Das Opaque-Pointer-Muster trennt die Deklaration eines Typs von seiner Definition und verbirgt so die Implementierungsdetails.

Die Header-Datei enthält nur eine 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

Die Quelldatei enthält 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);
}

Die Struct-Definition erscheint nur in der .c-Datei. Dateien, die den Header einbinden, wissen, dass der Typ existiert, können aber nicht direkt auf seine Member zugreifen. Dies ermöglicht echte Kapselung in C.

Probier es selbst

#include <stdio.h>
#include "timer.h"

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: Erstelle einen Timer mit dem bereitgestellten seconds-Wert
    
    // TODO: Gib "Timer created" aus
    
    // TODO: Gib den Timer frei
    
    // TODO: Gib "Timer freed" aus
    
    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 Object Oriented Programming