C++ gotcha: funkcje wirtualne i argumenty domyślne

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.

Leave a Reply

Your email address will not be published.