Dziedziczenie, obiekt klasy pochodnej jako argument klasy bazowej?

0

Cześć, mam problem ze zrozumieniem pewnej kwestii w CPP.
Mamy dwie klasy: Person (bazowa) oraz Student (pochodna).
Tak wygląda kod:

 

#include <iostream>

using namespace std;

class Person
{
public:
    void introduce()
    {
        cout << "Hey from Person!" << endl;
    }
};

class Student: public Person
{
public:
    void introduce()
    {
        cout << "Hey from Student!" << endl;
    }
};

void who (Person p)
{
    p.introduce();
}

int main()
{
    Student john;
    john.introduce();
    who(john);
    return 0;
}

output będzie wyglądał tak:
Hey from Student! Hey from Person!

Dlaczego funkcja who() która przyjmuje jako argument obiekt klasy Person może zostać wywołana kiedy przesyłam jej jako argument obiekt klasy Student? Jak się nazywa taka operacja? Do czego się to stosuje?

1

Abstrakcja. Możesz też zastosować interfejs(c++: virtual, c#: interface), który stosuje się do metod o których wiemy, że są z jednej "parafii" i nie przypisujemy im konkretnych funkcji. Czyli każdy obiekt z abstrakcją np. "Person" będzie mógł mieć interfejs "WhoAmI". Przydaje się to np. w pętlach.

1

Masz publiczne dziedziczenie, co oznacza, że każdy obiekt klasy Student jest obiektem klasy Person. To się przydaje przy polimorfizmie.

2

Warto zauważyć, że w tym przykładzie polimorfizm nie wystąpi - zadziała jedynie przekazując referencję/wskaźnik.

3

Standardowy przykład. Dajmy na to, że tworzysz prosty program graficzny (obiektowo), który ma rysować figury geometryczne. Masz obiekt klasy Painter:

class Painter
{
public:
  void Draw(int x, int y, const Shape & s)
  {
    s.DoDraw(x, y);
  }
}
 

Painter ma za zadanie "powiedzieć" innym obiektom, że mają się rysować i gdzie. Klasa shape jest klasą abstrakcyjną (nie możesz narysować "kształtu". Bo czym jest kształt?)

class Shape
{
protected:
  virtual void DoDraw(int x, int y) = 0;

friend class Painter;
}

Tutaj trochę wybiegłem. Jeśli nie wiesz, co to friend, to nie zwracaj uwagi. Równie dobrze może tego nie być, a DoDraw może być metodą publiczną, a nie chronioną. W każdym razie, jak już mówiłem "kształtu" jako takiego nie możesz narysować (no bo jak?). Ale kształt jako taki stanowi świetną bazę do tych Twoich figur. Weźmy np. kółko:

 
class Circle: protected Shape
{
protected:
    virtual void DoDraw(int x, int i) override
    {
        //tutaj w magiczny sposób rysujemy kółko
    }
}

Dalej możemy mieć kwadrat:

 
class Square: protected Shape
{
protected:
    virtual void DoDraw(int x, int i) override
    {
        //tutaj w magiczny sposób rysujemy kwadrat
    }
}

(mogłem coś pomylić ze specyfikatorami dostępu, ale piszę z głowy)

Dzięki czemu w klasie Painter nie musisz mieć osobnych funkcji do rysowania każdej figury. Każda figura wie sama, jak ma się narysować. Magia :)
A czemu nie chcesz osobnych metod do rysowania każdej figury? Bo to złe. Wyobraź sobie, że masz teraz rysować trójkąt. No to co. Musisz najpierw rozkminić obiekt Painter, potem go zmienić w taki sposób, żeby umiał narysować trójkąt. Ale po co tak? Jest to zła zmiana. Lepiej utworzyć nową klasę o nazwie Triangle dziedziczącą po Shape.

I dzięki temu możesz zrobić coś takiego:

 
Painter p;
Circle c;
Square sq;

p.Draw(0, 0, c); //i kółko narysowane
p.Draw(10, 10, sq); //i kwadrat narysowany

//a teraz tworzysz gdzieś nową klasę do rysowania trójkątów i:
Triangle t;
p.Draw(100, 100, t); //i trójkąt narysowany.

A możesz zrobić to wszystko dzięki temu, że twoje kółko jest kształtem. Kwadrat jest kształtem. Trójkąt też jest kształtem (wszystko dziedziczy po Shape, a więc jest w pewnym sensie obiektem typu Shape). Rozumiesz?

0

Bardzo dziękuję za odpowiedzi :) Już wszystko jasne.

1 użytkowników online, w tym zalogowanych: 0, gości: 1