W wprowadzenie do dziedziczenia lekcja wspomnieliśmy, że jedną z największych korzyści płynących z używania klas pochodnych jest możliwość ponownego wykorzystania już napisanego kodu. Możesz dziedziczyć funkcjonalność klasy bazowej, a następnie dodać nową funkcjonalność, zmodyfikować istniejącą funkcjonalność lub ukryć funkcjonalność, której nie chcesz. W tej i kilku następnych lekcjach przyjrzymy się bliżej, jak każda z tych rzeczy jest wykonywana.
Najpierw zacznijmy od prostej klasy bazowej:
#include <iostream>
class Base
{
protected:
int m_value {};
public:
Base(int value)
: m_value { value }
{
}
void identify() const { std::cout << "I am a Base\n"; }
};Teraz utwórzmy klasę pochodną, która dziedziczy z Base. Ponieważ chcemy, aby klasa pochodna mogła ustawić wartość m_value podczas tworzenia instancji obiektów pochodnych, sprawimy, że konstruktor Derived wywoła konstruktor Base na liście inicjalizacyjnej.
class Derived: public Base
{
public:
Derived(int value)
: Base { value }
{
}
};Dodawanie nowej funkcjonalności do klasy pochodnej
W powyższym przykładzie, ponieważ mamy dostęp do kodu źródłowego klasy Base, możemy, jeśli chcemy, dodać funkcjonalność bezpośrednio do Base.
Może się zdarzyć, że będziemy mieli dostęp do klasy bazowej, ale nie chcę jej modyfikować. Rozważmy przypadek, w którym właśnie kupiłeś bibliotekę kodu od zewnętrznego dostawcy, ale potrzebujesz dodatkowej funkcjonalności. Można dodać coś do oryginalnego kodu, ale nie jest to najlepsze rozwiązanie. Co się stanie, jeśli sprzedawca prześle Ci aktualizację? Albo Twoje dodatki zostaną nadpisane, albo będziesz musiał ręcznie przenieść je do aktualizacji, co jest czasochłonne i ryzykowne.
Alternatywnie może się zdarzyć, że modyfikacja klasy bazowej nie będzie nawet możliwa. Rozważmy kod w bibliotece standardowej. Nie jesteśmy w stanie modyfikować kodu będącego częścią biblioteki standardowej. Możemy jednak dziedziczyć po tych klasach, a następnie dodawać własne funkcjonalności do klas pochodnych. To samo dotyczy bibliotek innych firm, w których dostarczane są nagłówki, ale kod jest prekompilowany.
W obu przypadkach najlepszą odpowiedzią jest wyprowadzenie własnej klasy i dodanie żądanej funkcjonalności do klasy pochodnej.
Jednym z oczywistych pominięć w klasie Base jest sposób, w jaki publiczny dostęp do m_value. Moglibyśmy temu zaradzić, dodając funkcję dostępu w klasie Base — ale dla przykładu zamiast tego dodamy ją do klasy pochodnej. Ponieważ wartość m_value została zadeklarowana jako chroniona w klasie Base, Derived ma do niej bezpośredni dostęp.
Aby dodać nową funkcjonalność do klasy pochodnej, po prostu zadeklaruj tę funkcjonalność w klasie pochodnej jak zwykle:
class Derived: public Base
{
public:
Derived(int value)
: Base { value }
{
}
int getValue() const { return m_value; }
};Teraz publiczność będzie mogła wywołać funkcję getValue() na obiekcie typu Derived, aby uzyskać dostęp do wartości m_value.
int main()
{
Derived derived { 5 };
std::cout << "derived has value " << derived.getValue() << '\n';
return 0;
}Daje to wynik:
derived has value 5
Chociaż może to być oczywiste, obiekty typu Base nie mają dostępu do funkcja getValue() w Derived. Poniższe czynności nie działają:
int main()
{
Base base { 5 };
std::cout << "base has value " << base.getValue() << '\n';
return 0;
}Dzieje się tak, ponieważ w Base nie ma funkcji getValue(). Funkcja getValue() należy do funkcji Derived. Ponieważ Derived jest bazą, Derived ma dostęp do rzeczy w Base. Jednak Base nie ma dostępu do niczego w Derived.

