Run Code  | API  | Code Wall  | Misc  | Feedback  | Login  | Theme  | Privacy  | Patreon 

Polimorfizm :)

#include <iostream>

class Base
{
protected: // Jeżeli Value byłoby private to nie moglibyśmy go użyć w klasach dziedziczących z Base
    int Value;
public:
    void SetValue(int NewValue) //Metoda niewirtualna - nie można przeciążyć jej w klasach dziedziczących
    {
        Value=NewValue;
    }
    virtual int GetValue() //Metoda wirtualna - można ją przeciążyć w klasach dziedziczących
    {
        return Value;
    }
};

class Div_A : public Base
{
    int GetValue() //Metoda niewirtualna - nie można przeciążyć jej w klasach dziedziczących
                   //Ale jest wirtualna w klasie bazowej dlatego możemy ją przeciążyć
    {
        return Value+1;
    }
    void Write()
    {
        std::cout<<"Hallo world!\n";
    }
};

class Div_B : public Base
{
    int GetValue() //Jak wyżej ^
    {
        return Value+Value;
    }
};

class Div_C : public Base
{
    int GetValue() //Jak wyżej ^
    {
        return Value*Value;
    }
};

    
int main()
{
    Base* Tab[3]; //Trzy elementowa tablica wskaźników na obiekty klasy Base
    Tab[0] = new Div_A;
    Tab[1] = new Div_B;
    Tab[2] = new Div_C;
    // Jeżeli chcemy dostać obiekt, na jaki pokazuje wskaźnik zawarty np. w Tab[0]
    // to używamy operatora dereferencji (wyłuskania) '*'
    // Mamy zatem *Tab[0]
    // Teraz dopiero możemy wywołać metodę jaka jest zawarta wewnątrz obiektu jaki uzyskaliśmy
    // czyt. taki zapis Tab[0].SetValue(10); nie ma sensu bo wskaźnik Tab[0] zawiera tylko adres.. 
    // a adres nie jest obiektem, który wewnątrz zawiera jakiekolwiek metody (to tylko jakaś liczba)
    // Trzeba zatem najpierw "wydobyć" obiekt na jaki pokazuje wskaźnik, a dopiero potem wywołać jego metodę
    // A więc:
    (*Tab[0]).SetValue(10);
    // Jednak programiści uznali, że taki zapis (mimo, że jest prawidłowy) to jest brzydki i mało czytelny
    // Dlatego można zastąpić te nawiasy, kropki i gwiazdeczki czymś takim:
    Tab[1]->SetValue(10);
    // Właśnie wywołałem metodę obiektu na jaki pokazuje wkaźnik Tab[1]
    // Jako że Tab to tablica wskaźników na klasę Base to możemy wywołać przy pomocy tych wskaźników
    // tylko metody znajdujące się w klasie Base! (Jeżeli jakaś metoda nie znajdowałaby się w klasie Base to wywołanie jej byłoby niemożliwe):
    // Np. zapis: Tab[0]->Write(); wywoła błąd (co prawda metoda Write znajduje się w obiekcie typu Div_A ale nie znajduje się wewnątrz klasy Base, a wskaźnik jest właśnie typu Base)
    // Jednak jeżeli:
    // 1. Jakaś metoda w klasie Base jest wirtualna i w klasach dziedziczących po niej zostanie ona nadpisana
    // 2. oraz w rzeczywistości wskaźnik mimo, że jest typu Base zawiera obiekt dziedziczący z klasy Base
    // (W tym wypadku Tab[1] zawiera adres obiektu typu Div_B, a nie czystego Base)
    // Wtedy jeżeli zostanie wywołana metoda wirtualna kompilator ogarnie, że ma zostać wywołana metoda nadpisana w klasie dziedziczącej
    // tak więc mając zapis:
    Tab[2]->SetValue(10); // Tutaj wywołujemy metodę niewirtualną; nienadpisaną, która znajduje się tylko w klasie bazowej
    for(int i=0; i<3; ++i)
    {
        std::cout<<Tab[i]->GetValue()<<' '; //Tutaj wywołujemy metodę GetValue, która normalnie znajduje się w klasie Base, ale posiada różne
                                            //nadpisane wersje. Zależnie od obiektu dziedziczącego wywoła inny efekt.
    }
    //Teraz naciśnij [Run it (F8)] i zobacz efekt ;)
    
    for(int i=0; i<3; ++i) delete Tab[i]; // Bo zawsze należy po sobie posprzątać ^^
}
 run  | edit  | history  | help 0