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:

  1. Dodaj więcej figur - możliwości są tu nieograniczone.
  2. Dodaj więcej funkcji, np. obwód.
  3. Dodaj funkcję działającą tylko na jednej figurze (np. średnica) i sygnalizującą błąd przy innych.
  4. Użyj typu char dla identyfikatora i float dla wszystkich wymiarów.
  5. Wymyśl inne zastosowanie tego mechanizmu, poza geometrią.

0 komentarzy