Iteracja polimorficzna
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 42 z 61.
Nauczyłeś się tworzyć interfejsy i implementować je za pomocą różnych konkretnych funkcji. Prawdziwa korzyść pojawia się wtedy, gdy możesz traktować kolekcję różnych obiektów jednolicie: iterować po nich i wywoływać na każdym ten sam wskaźnik do funkcji, niezależnie od tego, jakie konkretne zachowanie jest z nim powiązane.
Wyobraź sobie, że masz tablicę struktur Action, z których każda ma przypisaną własną funkcję execute:
typedef void (*ExecuteFunc)(void);
typedef struct {
const char* name;
ExecuteFunc execute;
} Action;
void jump(void) { printf("Jumping!\n"); }
void run(void) { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }Możesz utworzyć tablicę tych działań, z których każde jest powiązane z inną funkcją, a następnie przejść przez nią za pomocą prostej pętli for:
int main() {
Action actions[3] = {
{ "Jump", jump },
{ "Run", run },
{ "Rest", rest }
};
for (int i = 0; i < 3; i++) {
printf("%s: ", actions[i].name);
actions[i].execute();
}
return 0;
}Pętla nie wie ani nie interesuje się tym, co każda akcja robi wewnętrznie. Po prostu wywołuje execute() dla każdego elementu.
To polimorfizm w działaniu: ten sam kod obsługuje obiekty o zupełnie różnych zachowaniach. Dodanie nowego typu działania nie wymaga żadnych zmian w logice iteracji; wystarczy dodać kolejny element do tablicy.
Wyzwanie
ŁatwyZbudujmy system zarządzania Task, który demonstruje iterację polimorficzną: przetwarzanie kolekcji obiektów współdzielących wspólny interfejs, ale wykonujących różne działania.
Rozmieścisz kod w trzech plikach:
task.h: Zdefiniuj tutaj interfejs zadań. Utwórz typ wskaźnika do funkcji o nazwieTaskFunc, która nie przyjmuje parametrów i niczego nie zwraca. Następnie zdefiniuj strukturęTaskzawierającąname(typuconst char*) oraz wskaźnik do funkcjiruntypuTaskFunc.task.c: Zaimplementuj trzy różne funkcje zadań reprezentujące różne rodzaje pracy:backup_task: wyświetlaBacking up data...cleanup_task: wyświetlaCleaning up files...report_task: wyświetlaGenerating report...
main.c: Połącz tutaj wszystkie elementy. Utwórz tablicę strukturTask, z których każda jest powiązana z inną funkcją zadania. Następnie wykonaj iterację po tablicy i uruchom każde zadanie polimorficznie; dla każdego zadania wyświetl jego nazwę, a następnie wywołaj jego funkcjęrun.
Program otrzyma jedno dane wejściowe: liczbę zadań do uruchomienia (1, 2 lub 3).
Utwórz tablicę zawierającą dokładnie trzy zadania w następującej kolejności: zadanie tworzenia kopii zapasowej o nazwie Backup, zadanie czyszczenia o nazwie Cleanup oraz zadanie generowania raportu o nazwie Report. Na podstawie podanej liczby wykonaj iterację tylko po tej liczbie zadań, zaczynając od początku tablicy.
Dla każdego zadania w iteracji wyświetl jego nazwę, a po niej dwukropek i spację, a następnie wywołaj jego funkcję run.
Przykładowy wynik, gdy dane wejściowe to 2:
Backup: Backing up data...
Cleanup: Cleaning up files...Przykładowy wynik, gdy dane wejściowe to 3:
Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...Kluczowa wskazówka jest taka, że pętla nie musi wiedzieć, co robi każde zadanie. Po prostu wyświetla nazwę i wywołuje run() dla każdego elementu. Ten sam kod iteracji obsługuje jednakowo wszystkie typy zadań. Pamiętaj, aby użyć osłon przed wielokrotnym dołączaniem w pliku nagłówkowym.
Spróbuj swoich sił
#include <stdio.h>
#include "task.h"
// Zadeklaruj funkcje zadań zaimplementowane w task.c
void backup_task(void);
void cleanup_task(void);
void report_task(void);
int main() {
int count;
scanf("%d", &count);
// TODO: Utwórz tablicę 3 struktur Task w tej kolejności:
// 1. Zadanie tworzenia kopii zapasowej o nazwie "Backup", używające backup_task
// 2. Zadanie czyszczenia o nazwie "Cleanup", używające cleanup_task
// 3. Zadanie tworzenia raportu o nazwie "Report", używające report_task
// TODO: Przejdź przez 'count' zadań z tablicy
// Dla każdego zadania wypisz: "<name>: ", a następnie wywołaj jego funkcję run
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania modularnego
Pliki nagłówkoweZabezpieczenia przed wielokrotnym dołączeniemPliki źródłoweFunkcje statycznePodsumowanie: kalkulator modularny4Enkapsulacja
Koncepcja niejawnych wskaźnikówDefiniowanie niejawnych strukturGettery i setteryWalidacja w setterachPowtórka: tajemnicze pudełko2Obiekty i metody
Struktury jako obiektyWskaźnik „self”Poprawność constWskaźnik czy wartośćMetody pomocniczePodsumowanie: menedżer punktów5Projekt: Proste konto bankowe
Konfiguracja projektuImplementacja konta8Polimorfizm
Wskaźniki do funkcji w strukturachSymulowanie metodKoncepcja interfejsuImplementowanie interfejsówIteracja polimorficznaPodsumowanie: Greeter11Wzorce projektowe w C
Wzorzec SingletonWzorzec fabrykiWzorzec iteratoraPodsumowanie: fabryka loggera3Cykl życia obiektu
Wzorzec konstruktoraWzorzec destruktoraInicjalizacja na stosieKopia głębokaPodsumowanie: klasa opakowująca ciąg znaków6Dziedziczenie przez kompozycję
Osadzanie strukturZasada pierwszego elementuDostęp do elementów klasy nadrzędnejKonwersja w górę hierarchiiPodsumowanie: hierarchia kształtów9Projekt: Rysownik kształtów
Przegląd projektuImplementacja kołaImplementacja prostokątaZastosowanie polimorfizmuKontener kształtówPoćwicz samodzielnie: Kompilator C online