Kiedy w klasie pochodnej piszemy nową wersję funkcji wirtualnej, to w C++ powinniśmy zawsze uważać na to, żeby jej nagłówek był identyczny z tym, który zdeklarowano w klasie bazowej. To dlatego, że składnia języka nie wymaga w tej sytuacji żadnego słowa kluczowego w rodzaju override
. Jeśli więc coś nieopatrznie zmienimy, to nie uzyskamy nowej wersji funkcji, tylko zupełnie nową funkcję przeciążoną:
Taki efekt jest jest w gruncie rzeczy oczywisty i łatwy do zrozumienia. Pewnie więc będzie nieco zaskakujące dowiedzieć się, że tak naprawdę postać funkcji wirtualnej w klasie pochodnej może się trochę różnić, a mimo to wciąż będziemy mówili o tej samej funkcji.
Jak to jest możliwe?… Dopuszcza się mianowicie różnicę w typie zwracanym przez nową wersję funkcji wirtualnej. Może on być inny od typu zwracanego przez wersję z klasy bazowej – ale tylko pod warunkiem, że:
int
)const
/volatile
co typ pierwotnyPrzykładowo więc, jeśli w klasie bazowej funkcja wirtualna zwraca const A*
, to w klasie pochodnej może zwracać const B*
, o ile klasa B
dziedziczy publicznie po A
. Nie może za to zwracać samego B*
(niestałego) lub const X*
, gdy klasa X
jest niezwiązana z A
.
Do czego taki “myk” może się przydać? Najczęściej chodzi tutaj o sytuacje, gdy mamy do czynienia z równoległym dziedziczeniem kilku klas, które na każdym poziomie są związane ze sobą. Mogę sobie na przykład wyobrazić ogólny menedżer zasobów w grze, którego metoda wirtualna Get
zwraca wskaźnik na bazową klasę Resource
, a następnie bardziej wyspecjalizowany TextureManager
, który w tej sytuacji podaje nam wskaźnik na Texture
. (Oczywiście klasa Texture
w założeniu dziedziczy tu po Resource
). Czasami coś takiego może być potrzebne, aczkolwiek takie równoległe hierarchie dziedziczenia nie są specjalnie eleganckie ani odporne na błędy.
Lepszym przykładem jest wirtualny konstruktor kopiujący: metoda, która pozwala na wykonanie kopii obiektu bez dokładnej znajomości jego typu. Zwyczajowo nazywa się ją Clone
:
Dzięki temu że metoda jest wirtualna, można ją wywołać nie znając rzeczywistego typu obiektu (co nie jest możliwe w przypadku zwykłych konstruktorów, które w C++ wirtualne być nie mogą). W wyniku jej wywołania otrzymamy jednak tylko ogólny wskaźnik Object*
na powstałą kopię obiektu.
Gdybyśmy teraz nie zmienili typu zwracanego przez metodę w klasie pochodnej, to klonowanie podobne do tego:
wymagałoby dodatkowego rzutowania w górę, by mogło zostać przepuszczone przez kompilator.
Adding comments is disabled.
Comments are disabled.