Dziedziczenie

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.

is-a

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.