Dziedziczenie publiczne, inicjowanie klasy bazowej, kolejność wywoływania konstruktorów i destruktorów, typ double.
Obok poznanej w poprzednim rozdziale relacji "A ma B", w języku C++ niezwykle ważne miejsce zajmuje relacja "A jest B". Gdy w języku naturalnym mówimy, iż gwiazda jest ciałem niebieskim, mamy na myśli to, iż gwiazda posiada wszystkie właściwości ciał niebieskich i - być może - jakieś inne, specyficzne cechy, odróżniające ją od innych ciał niebieskich. W języku C++ związek tego typu wyrażamy poprzez tzw. dziedziczenie (ang. inheritance). Załóżmy, iż gwiazdy reprezentujemy jako obiekty klasy Star, a ciała niebieskie - jako obiekty klasy CelestialBody. Mówimy, że klasa Star dziedziczy właściwości klasy CelestialBody lub że jest klasą pochodną klasy CelestialBody, lub że klasa CelestialBody jest klasą bazową klasy Star.
Rysunek 5. Reprezentacja graficzna relacji "A jest B".
Oto prosty przykład zastosowania dziedziczenia:
#include <iostream> class CelestialBody // Klasa ciał niebieskich { public: CelestialBody (double mass) : _mass (mass) { std::cout << "Creating celestial body of mass " << _mass << "\n"; } ~CelestialBody () { std::cout << "Destroying celestial body of mass " << _mass << "\n"; } private: const double _mass; }; class Star: public CelestialBody // Klasa gwiazd. Gwiazdy są są ciałami niebieskimi { public: Star (double mass, double brightness) : CelestialBody (mass), _brightness (brightness) { std::cout << "Creating a star of brightness " << _brightness << "\n"; } ~Star () { std::cout << "Destroying a star of brightness " << _brightness << "\n"; } private: const double _brightness; }; int main () { std::cout << " Entering main.\n"; Star aStar ( 1234.5, 0.1 ); std::cout << " Exiting main.\n"; } |
Wiersz
class Star: public CelestialBody |
informuje kompilator, że obiekty klasy Star dziedziczą wszystkie właściwości z klasy CelestialBody. W szczególności, skoro obiekty klasy CelestialBody w składowej _mass przechowują informację o swojej masie, obiekty klasy Star również będą posiadać masę.
W klasie CelestialBody zdefiniowano konstruktor, wymagający podania argumentu typu double. Typ double jest jednym z typów wbudowanych, odpowiadającym liczbom rzeczywistym zapisanym w reprezentacji zmiennoprzecinkowej o podwójnej precyzji. Istnieje też mniej dokładny typ zmiennoprzecinkowy, zwany float. Warto zwrócić uwagę na to, iż nasz obiekt std::cout bez najmniejszych problemów wyświetla wartość zmiennych typu doubles.
W preambule konstruktora klasy Star musimy wyspecyfikować argument, który zostanie przekazany konstruktorowi klasy bazowej -- CelestialBody. W celu poinformowania kompilatora, iż chcemy przekazać parametry do konstruktora klasy bazowej, w preambule klasy nadrzędnej posługujemy się nazwą klasy bazowej:
Star (double mass, double brightness) : CelestialBody (mass), _brightness (brightness) |
Niezwykle ważna jest kolejność wywoływania konstruktorów. Obowiązuje następująca reguła:
Najpierw pełnej konstrukcji podlega fragment obiektu, należący do klasy bazowej, a dopiero później konstruowana jest jego pozostała część. |
Po zakończeniu konstrukcji klasy bazowej, konstrukcja klasy pochodnej przebiega zgodnie z regułami omówionymi w poprzednim rozdziale: najpierw inicjowane są obiekty składowe, na koniec zaś wykonywany jest kod konstruktora klasy pochodnej. Jak już się Czytelnik zapewnie domyśla, porządek umieszczania informacji w preambule nie ma żadnego wpływu na kolejność inicjowania poszczególnych składowych klasy pochodnej i jeżeli ani klasa bazowa, ani żadna ze składowych klasy pochodnej nie wymaga jawnej konstrukcji, preambułę można w ogóle pominąć. Destruktory wywoływane są oczywiście w dokładnie odwrotnej kolejności, niż odpowiednie konstruktory: najpierw wywoływany jest destruktor klasy pochodnej, potem destruktory jej obiektów składowych, a na koniec - destruktor klasy podstawowej.