Menu
Coddy logo textTech

Definiowanie niejawnych struktur

Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 18 z 61.

Teraz, gdy rozumiesz pojęcie nieprzezroczystych wskaźników, zobaczmy, jak uzupełnić ten wzorzec. Nagłówek deklaruje istnienie typu, ale to w pliku źródłowym definiujesz, co tak naprawdę się w nim znajduje.

Oto kompletna konfiguracja. Najpierw nagłówek zawierający tylko deklarację wstępną:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Typ niekompletny

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

Następnie plik źródłowy zawiera właściwą definicję struktury:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // Ukryte przed dostępem z zewnątrz!
    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);
}

Najważniejsze: struct Counter { ... } pojawia się tylko w pliku .c. Każdy plik dołączający counter.h wie, że Counter istnieje, ale nie widzi value ani step. Próba zapisania c->value w main.c spowodowałaby błąd kompilatora: składowe są naprawdę niewidoczne.

Ten podział stanowi podstawę enkapsulacji w C. Kolejnym krokiem jest zapewnienie kontrolowanego dostępu za pomocą funkcji pobierających i ustawiających.

challenge icon

Wyzwanie

Łatwy

Zbudujmy moduł Timer, który demonstruje wzorzec nieprzezroczystego wskaźnika. Poćwiczysz ukrywanie definicji struktury w pliku źródłowym, dzięki czemu jej wewnętrzne składowe będą całkowicie niewidoczne dla kodu korzystającego z modułu.

Utworzysz trzy pliki:

  • timer.h: To Twój publiczny interfejs. Zadeklaruj nieprzezroczysty typ Timer, używając wyłącznie deklaracji wstępnej: bez definicji ciała struktury! Zadeklaruj też dwie funkcje: create_timer (przyjmuje liczbę całkowitą określającą sekundy i zwraca wskaźnik do Timer) oraz free_timer (przyjmuje wskaźnik do Timer i zwalnia zasoby). Użyj strażników dołączania z symbolem TIMER_H.
  • timer.c: Tutaj kryje się sekret. Zdefiniuj właściwą struct Timer z dwiema ukrytymi składowymi: int seconds i int running (użyj wartości 1 dla działania i 0 dla zatrzymania). Zaimplementuj konstruktor, który przydzieli pamięć dla struktury, zainicjuje seconds podaną wartością i ustawi running na 1. Zaimplementuj destruktor, który zwolni pamięć.
  • main.c: Użyj modułu Timer, aby utworzyć timer i potwierdzić, że został utworzony pomyślnie. Ponieważ składowe struktury są ukryte, nie możesz uzyskać do nich bezpośredniego dostępu. Na tym właśnie polega cały sens! Po prostu wypisz komunikat potwierdzający utworzenie, następnie zwolnij timer i wypisz komunikat o zwolnieniu zasobów.

Otrzymasz jedno wejście: liczbę sekund (liczbę całkowitą), od której należy zainicjować timer.

W pliku głównym utwórz timer z podaną liczbą sekund. Wypisz komunikat o utworzeniu, następnie zwolnij timer i wypisz potwierdzenie.

Wypisz dane wyjściowe w tym formacie:

Timer created
Timer freed

Najważniejsze jest to, że main.c może korzystać ze wskaźników do Timer, ale nie ma możliwości bezpośredniego dostępu do seconds ani running. Próba zapisania t->seconds w main.c spowodowałaby błąd kompilatora, ponieważ ciało struktury jest zdefiniowane wyłącznie w timer.c. To właśnie prawdziwa enkapsulacja: szczegóły implementacji są całkowicie ukryte przed użytkownikami modułu.

Spróbuj swoich sił

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

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: Utwórz timer z podaną wartością seconds
    
    // TODO: Wypisz "Timer created"
    
    // TODO: Zwolnij pamięć timera
    
    // TODO: Wypisz "Timer freed"
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C online