Weźmy za przykład następujące klasy:
struct base { virtual void foo(int value = 42) { cout << __PRETTY_FUNCTION__ << ": " << value << endl; } }; struct derived : base { virtual void foo(int value = 100) override { cout << __PRETTY_FUNCTION__ << ": " << value << endl; } }; |
Nic niezwykłego, typowy dynamiczny polimorfizm. Każdy znający podstawy C++ powinien zdawać sobie sprawę, że w poniższym kodzie dwukrotnie zostanie wywołana funkcja derived::foo:
derived obj; base* b = &obj; derived* d = &obj; b->foo(); d->foo(); |
Zaskakujące może jednak być faktyczne wyjście:
virtual void derived::foo(int): 42 virtual void derived::foo(int): 100 |
Dlaczego wywołania, które powinny być identyczne jednak takie nie są? Jest tak ponieważ wartości domyślne argumentów funkcji (nawet wirtualnych) odpowiadają argumentom domyślnym statycznego typu wskaźnika/referencji.
W prostszych słowach, ponieważ b jest typu base*, wywołanie b->foo() jest równoznaczne z wywołaniem b->foo(42), ponieważ taki jest domyślny argument zdefiniowany w base. Z tego samego powodu, ponieważ d jest derived*, wywołanie d->foo() jest równoznaczne z d->foo(100).
Podsumowując: istotny tutaj jest typ statyczny (wskaźnika/referencji), a nie faktyczny (dynamiczny) typ obiektu, który się za nim kryje – odwrotność polimorfizmu dynamicznego.