Dziedziczenie w języku C
W pierwszej części cyklu została podana definicja obiektu podstawowego. Zajmujemy się (dla przypomnienia) programowaniem obiektowym w języku C. Skoro tak, to wypadało by pokazać, jak tworzyć kolejne definicje obiektów. W tej części zajmiemy się dziedziczeniem. Najlepiej to będzie prześledzić na obiecanym w poprzedniej części przykładzie. Dla większej czytelności przedstawię tu jedynie najważniejsze części programu. Calość mogą Państwo pobrać i przetestować.Warto wspomnieć, że autor zezwala na modyfikacje kodu jak również użycie go we własnych projektach (nawet komercyjnych), z jednym zastrzeżeniem - wprowadzenia informacji do dokumentacji o tym, że używany jest w programie kod Tomasza Kaczanowskiego. Miło też będzie autorowi, jeśli dostanie taką informację drogą mailową. Ale wracajmy może do tematu.
Zacznijmy od definicji struktur. Powiedzmy, że zdefiniujemy sobie 2 testowe typy. Jeden na przykład będzie przechowywał tekst, drugi natomiast liczbę typu całkowitego (int).
struct myTextObj { ObjectDefaultDestructor fooDestructor; ObjectDefaultGetSize fooGetSize; ObjectDefaultClone fooCloneObj; char *buftext; }; struct myIntegerObj { ObjectDefaultDestructor fooDestructor; ObjectDefaultGetSize fooGetSize; ObjectDefaultClone fooCloneObj; int ivalue; };Do kodu przedstawionego w poprzednim odcinku pozwoliłem sobie dodać warunkowe wyświetlanie informacji o wykonywanym kodzie, to pozwoli lepiej zilustrować działanie przedstawionego tutaj programu.
Na potrzeby konstruktora kopiującego potrzebujemy zdefiniować: dla pierwszego przypadku metodę kopiującą, dla drugiego typu potrzebna natomiast jest funkcja zwracająca wielkość obiektu. Dla typu myTextObj będziemy musieli zdefiniować ponadto własny destruktor. Czyli potrzebne nam będą funcje:
struct TDefaultObj *myTextObjClone(struct TDefaultObj *aval); size_t myIntegerObjGetSize(struct TDefaultObj *aval); void myTextObjDestructor(struct TDefaultObj *aval);Analizując kod nietrudno zauważyć, iż dziedziczenie realizowane jest w taki sposób, że pierwsze pola obiektu potomnego są identyczne jak w obiekcie nadrzędnym. Metody obiektu to wskaźniki na funkcje, gdzie jednym z parametrów jest wskaźnik na obiekt, dla którego dana funkcja jest wywoływana.
W przykładowym programie zdefiniowałem także funkcje tworzące obiekty, w których przypisujemy odpowiednie metody i wartości do odpowiednich pól obiektu. Funkcje te są konstruktorami obiektów. Teoretycznie takie przypisania możnaby zrobić w kodzie głównym programu, jednak zaprezentowane tu podejście pozwala na więcej (zalety takiego podejścia przedstawię w odcinku poświęconym hermetyzacji).
struct myTextObj *CreateTextObj(const char* astr) { struct myTextObj *lres = (struct myTextObj *)malloc(sizeof(struct myTextObj)); if (lres) { lres->buftext = (char *)malloc(strlen(astr) + 1); if (lres->buftext) strcpy(lres->buftext, astr); lres->fooDestructor = myTextObjDestructor; lres->fooGetSize = NULL; lres->fooCloneObj = myTextObjClone; } return lres; } struct myIntegerObj *CreateIntegerObj(int aval) { struct myIntegerObj *lres = (struct myIntegerObj *)malloc(sizeof(struct myTextObj)); if (lres) { lres->ivalue = aval; lres->fooDestructor = NULL; lres->fooGetSize = myIntegerObjGetSize; lres->fooCloneObj = NULL; } return lres; }Konstruktory mają za zadanie przydzielenie pamięci na obiekt, oraz na dodatkowe dane (obiekt tekstowy ma dodatkowo bufor na tekst, ustawiany w konstruktorze). Dodatkowo jak widać, przypisane są odpowiednie funkcje do odpowiednich zmiennych. Metody nieobsługiwane mają ustawianą wartość NULL. Prosty program natomiast obrazuje nam jak tworzy się obiekty, oraz ich kopie.
int main() { struct myTextObj *txta, *txtb; struct myIntegerObj *iobja, *iobjb; txta = CreateTextObj("Mój Text 1"); iobja = CreateIntegerObj(7); txtb = (struct myTextObj *)CloneDefaultObject((struct TDefaultObj *)txta); iobjb = (struct myIntegerObj *)CloneDefaultObject((struct TDefaultObj *)iobja); FreeDefaultObject((struct TDefaultObj *)txtb); FreeDefaultObject((struct TDefaultObj *)iobjb); FreeDefaultObject((struct TDefaultObj *)iobja); FreeDefaultObject((struct TDefaultObj *)txta); return 0; }wynik działania takiego programu mamy:

Zachęcam do pobrania kodów źródłowych oraz zmian i własnych testów. No i oczywiście do zaglądania na stronę, bo kolejny odcinek już gdzieś się czai :)
Pozdrawiam
Tomasz Kaczanowski
Dziedziczenie język C C programowanie programowanie obiektowe