Polimorficzne "obiekty" w C
Manna5
Wyobraźmy sobie, że chcemy przetwarzać różnego typu figury geometryczne: kwadraty, trójkąty oraz koła. Nieważne zresztą co to za figura, nas interesuje jej pole. Tutaj nasuwa się użycie języka obiektowego, takiego jak Java, ale spróbujmy zrealizować to w czystym C. Jak? Otóż będziemy przekazywać nasze figury jako wskaźniki intowe. Pierwsza komórka będzie odpowiadać za rodzaj figury, a następne za jej wymiary, zależne od rodzaju. Nie zapomnijmy też dołączyć bibliotek alokacji pamięci oraz wejścia i wyjścia:
#include <stdio.h>
#include <stdlib.h>
#define idkwadrat 1
#define idtrojkat 2
#define idkolo 3
Teraz, jeszcze zanim zaczniemy liczyć pola, przydałaby się jakaś funkcja do tworzenia figur. Dla kwadratu będzie to:
int *utwkwadrat (int a)
{
int *wsk = calloc(2, sizeof(int));
if (wsk) {
wsk[0] = idkwadrat;
wsk[1] = a;
}
return wsk;
}
Ta funkcja rezerwuje pamięć na dwie komórki typu int - jedna będzie przechowywała identyfikator informujący, że jest to kwadrat, a druga długość boku tego kwadratu, niezbędną do wyznaczenia pola. Potem zapisujemy wspomniany identyfikator wraz z przekazanym jako argument bokiem. Na koniec zwracamy wskaźnik do tego "obiektu" (jeśli alokacja się udała). Dopiszmy podobne "konstruktory" dla trójkąta i koła:
int *utwtrojkat (int a, int h)
{
int *wsk = calloc(3, sizeof(int));
if (wsk) {
wsk[0] = idtrojkat;
wsk[1] = a; wsk[2] = h;
}
return wsk;
}
int *utwkolo (int r)
{
int *wsk = calloc(2, sizeof(int));
if (wsk) {
wsk[0] = idkolo;
wsk[1] = r;
}
return wsk;
}
No to nareszcie najciekawsza część - liczenie pola:
int pole (int *fig)
{
int typ = fig[0];
if (typ == idkwadrat)
return fig[1] * fig[1];
else if (typ == idtrojkat)
return (fig[1] * fig[2]) >> 2;
else if (typ == idkolo)
return fig[1] * 44 / 7;
else
return 0;
}
Powyższa funkcja najpierw sprawdza, jaka to figura, a potem oblicza pole i je zwraca. Gdy kod figury okaże się nieprawidłowy, zwraca 0. Przetestujmy działanie tej architektury, pamiętając o zwalnianiu pamięci:
int main ()
{
int *mojkwadrat;
mojkwadrat = utwkwadrat (5);
if (mojkwadrat) {
printf ("Pole kwadratu: %d\n", pole(mojkwadrat));
free (mojkwadrat);
}
int *mojtrojkat;
mojtrojkat = utwtrojkat (5, 4);
if (mojtrojkat) {
printf ("Pole trojkata: %d\n", pole(mojtrojkat));
free (mojtrojkat);
}
int *mojekolo;
mojekolo = utwkolo (5);
if (mojekolo) {
printf ("Pole kola: ok. %d\n", pole(mojekolo));
free (mojekolo);
}
return 0;
}
Kawałki kodu można skleić i skompilować, uzyskując działający program. Oto parę propozycji rozbudowy:
- Dodaj więcej figur - możliwości są tu nieograniczone.
- Dodaj więcej funkcji, np. obwód.
- Dodaj funkcję działającą tylko na jednej figurze (np. średnica) i sygnalizującą błąd przy innych.
- Użyj typu
char
dla identyfikatora ifloat
dla wszystkich wymiarów. - Wymyśl inne zastosowanie tego mechanizmu, poza geometrią.