﻿#include <iostream>
#include <vector>

using namespace std;

// klasa abstrakcyjna jest klasą zawierającą co najmniej jedną metodę abstrakcyjną – czysto wirtualną,
// może zawierać również zwykłe pola i metody niewirtualne

// klasa Zwierze jest abstrakcyjną klasą bazową z metodą czysto wirtualną 
// (interfejs - są tylko metody czysto wirtualne, brak pól, 
// mogą wystepować komponenty statyczne)
class Zwierze {
public:
    // metoda czysto wirtualna (pure virtual)
    // jest to deklaracja metody, bez definicji,
    // definicja powinna być określona w klasie pochodnej
    // (jeśli nie jest - wtedy klasa pochodna też jest abstrakcyjna)
    virtual void przedstawSie() = 0; 
    // wirtualny destruktor w klasie bazowej zapewnia poprawne usuwanie obiektów klas pochodnych 
    // przez wskaźnik do klasy bazowej. (usuń słowo virtual i zobacz co sie stanie: 
    // wywoływany jest jedynie destruktor klasy bazowej)
    virtual ~Zwierze() { cout << "destruktor Zwierze" << endl; };
};

// klasa z polami
class Nazwa {
public:
    string imie;
    Nazwa(string im) :imie(im) {};  // konstruktor parametrowy
    ~Nazwa() { cout << "destruktor Nazwa" << endl; };
};

// klasa pochodna z własną definicją metody wirtualnej
// wielodziedziczenie – klasa Lew dziedziczy z interfejsu metodę przedstawSie(),
// a z klasy Nazwa pole imie. 
class Lew : public Zwierze, public Nazwa {
public:
    Lew(string im) :Nazwa(im) {}; // konstruktor parametrowy
    // definicja metody wirualnej
    void przedstawSie() { cout << "Jestem lwem o imieniu " << imie << endl; };
    ~Lew() { cout << "destruktor Lew" << endl; };
};

// klasa pochodna z własną definicją metody wirtualnej
// wielodziedziczenie – klasa Chomik dziedziczy z interfejsu metodę przedstawSie(),
// a z klasy Nazwa pole imie
class Chomik : public Zwierze, public Nazwa {
public:
    Chomik(string im) :Nazwa(im) {};
    void przedstawSie() { cout << "Jestem chomikiem o imieniu " << imie << endl; };
    ~Chomik() { cout << "destruktor Chomik" << endl; };
};



int main()
{   
    cout << "Zwierzyniec:\n\n";
    // tworzymy instancje zwierząt na stercie
    Zwierze* lew = new Lew("Grozny");
    Zwierze* chomik = new Chomik("Malutki");

    // tablica ze wskaźnikami na kolejne zwierzęta
    vector<Zwierze*> zoo;
    zoo.push_back(lew);
    zoo.push_back(chomik);

    // dzięki hierarchii dzidziczenia
    // oraz słowu kluczowemu virtual
    // uzyskujemy właściwość polimorfizmu
    // dynamicznego(późne wiązanie)
    // kompilator przy wywołaniu metody
    // przedstawSie() uwzględnia
    // typ obiektu, na jaki wskazuje
    // wskaźnik (a nie typ wskaźnika)
    // dlatego każde zwierzę przedstawia się
    // osobiście

    // petla po wszystkich wskaźnikach na zwierzęta
    for (auto z : zoo) {
        z->przedstawSie();
    }

    // sprzątamy pamięć
    // dzięki wirtualnemu destruktorowi w klasie bazowej
    // usuwane są obiekty z pamięci we właściwej kolejności,
    // nie powstają wycieki pamięci 
    delete lew;
    delete chomik;
    lew = nullptr;
    chomik = nullptr;
}