Drobne programowanie

Kaczuś zaprasza do opowieści o algorytmach, językach programowania i strukturach danych

Na stronie stosowane są pliki cookies. Więcej na podstronie.
odsłon: 2626

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:
Wynik działania programu Pierwsze 2 linie to wynik dzialania konstruktorów kopiujących, kolejne 4 to wynik działania destruktora.
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

2013-08-30 21:18:02


Dziedziczenie język C C programowanie programowanie obiektowe