C++/Funkcje wirtualne: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Nie podano opisu zmian
DrJolo (dyskusja | edycje)
→‎Konsekwencje: import przykładu z artykułu polskiej wikipedii "Metoda wirtualna"
Linia 86:
 
Jak widać, za wirtualność się płaci - zarówno drobnym narzutem pamięciowym na każdy obiekt (identyfikator klasy), jak i drobnym narzutem czasowym (odnajdywanie przy każdym wywołaniu odpowiedniej klasy i jej funkcji składowej). Jednak zyskujemy możliwośc płynnego rozwoju naszego programu przez zastępowanie klas ich podklasami, co bez wirtualności jest niewykonalne. Przy możliwościach obecnych komputerów koszt wirtualności jest zaniedbywalny, ale wciąż warto przemyśleć, czy potrzebujemy wirtualności dla wszystkich funkcji.
 
==Przykład==
Poniższy program zawiera deklaracje 3 klas: <code>Figura</code>, <code>Kwadrat</code> i <code>Kolo</code>. W klasie <code>Figura</code> została zadeklarowana metoda wirtualna (słowo kluczowe ''virtual'') <code>virtual float pole()</code>. Każda z klas pochodnych od klasy <code>Figura</code> ma zaimplementowane swoje metody <code>float pole()</code>. Następnie (w funkcji main) znajdują się deklaracje obiektów każdej z klas i wskaźnika mogącego pokazywać na obiekty klasy bazowej <code>Figura</code>.
<source lang="cpp">
#include <iostream>
 
const float pi = 3.14159;
class Figura
{
public:
virtual float pole() const
{
return -1.0;
}
};
 
class Kwadrat : public Figura
{
public:
Kwadrat( const float bok ) : a( bok ) {}
 
float pole() const
{
return a * a;
}
private:
float a; // bok kwadratu
};
 
class Kolo : public Figura
{
public:
Kolo( const float promien ) : r( promien ) {}
 
float pole() const
{
return pi * r * r;
}
private:
float r; // promien kola
};
 
void wyswietlPole( Figura& figura )
{
std::cout << figura.pole() << std::endl;
return;
}
 
int main()
{
// deklaracje obiektow:
Figura jakasFigura;
Kwadrat jakisKwadrat( 5 );
Kolo jakiesKolo( 3 );
Figura* wskJakasFigura = 0; // deklaracja wskaźnika
 
// obiekty -------------------------------
std::cout << jakasFigura.pole() << std::endl; // wynik: -1
std::cout << jakisKwadrat.pole() << std::endl; // wynik: 25
std::cout << jakiesKolo.pole() << std::endl; // wynik: 28.274...
 
// wskazniki -----------------------------
wskJakasFigura = &jakasFigura;
std::cout << wskJakasFigura->pole() << std::endl; // wynik: -1
wskJakasFigura = &jakisKwadrat;
std::cout << wskJakasFigura->pole() << std::endl; // wynik: 25
wskJakasFigura = &jakiesKolo;
std::cout << wskJakasFigura->pole() << std::endl; // wynik: 28.274...
// referencje -----------------------------
wyswietlPole( jakasFigura ); // wynik: -1
wyswietlPole( jakisKwadrat ); // wynik: 25
wyswietlPole( jakiesKolo ); // wynik: 28.274...
 
return 0;
}
</source>
 
Wywołanie metod składowych dla każdego z obiektów powoduje wykonanie metody odpowiedniej dla klasy danego obiektu. Następnie wskaźnikowi <code>wskJakasFigura</code> zostaje przypisany adres obiektu <code>jakasFigura</code> i zostaje wywołana metoda <code>float pole()</code>. Wynikiem jest ''"-1"'' zgodnie z treścią metody <code>float pole()</code> w klasie <code>Figura</code>. Następnie przypisujemy wskaźnikowi adres obiektu klasy Kwadrat - możemy tak zrobić ponieważ klasa <code>Kwadrat</code> jest klasą pochodną od klasy <code>Figura</code> - jest to tzw. [[w:rzutowanie w górę|rzutowanie w górę]]. Wywołanie teraz metody <code>float pole()</code> dla wskaznika nie spowoduje wykonania metody zgodnej z typem wskaźnika - który jest typu <code>Figura*</code> lecz zgodnie z aktualnie wskazywanym obiektem, a więc wykonana zostanie metoda <code>float pole()</code> z klasy <code>Kwadrat</code> (gdyż ostatnie przypisanie wskaźnikowi wartości przypisywało mu adres obiektu klasy <code>Kwadrat</code>). Analogiczna sytuacja dzieje się gdy przypiszemy wskaźnikowi adres obiektu klasy <code>Kolo</code>. Następnie zostaje wykonana funkcja <code>void wyswietlPole(Figura&)</code> która przyjmuje jako parametr obiekt klasy <code>Figura</code> przez [[w:referencja|referencję]]. Tutaj również zostały wykonane odpowiednie metody dla obiektów klas pochodnych a nie metoda zgodna z obiektem jaki jest zadeklarowany jako parametr funkcji czyli <code>float Figura::pole()</code>. Takie działanie jest spowodowane przez przyjmowanie obiektu klasy <code>Figura</code> przez referencję. Gdyby obiekty były przyjmowane przez wartość (parametr bez ''&'') zostałaby wykonana 3 krotnie metoda <code>float Figura::pole()</code> i 3 krotnie wyświetlona wartość ''-1''.
 
Wyżej opisane działanie zostało spowodowane przez określenie metody w klasie bazowej jako '''wirtualnej'''. Gdyby zostało usunięte słowo kluczowe ''virtual'' w deklaracji metody w klasie bazowej, zostałyby wykonane metody zgodne z typem wskaźnika lub referencji, a więc za każdym razem zostałaby wykonana metoda <code>float pole()</code> z klasy <code>Figura</code>.
 
<noinclude>