w7 Funkcje wirtualne i polimorfizm(3)

background image

Funkcje wirtualne i polimorfizm

Wykład 7

background image

Mechanizm omawiany w ramach tematu funkcje
wirtualne decyduje o jednej z przewag programowania
obiektowego nad strukturalnym. Rozważmy dziedziczenie
w ramach klas (Grębosz ale wczesniej Eckel w Thinking
in Java):

Instrument: trąbka, bęben, fortepian

Przykład: plik nagłówkowy zawierający klasę z

wirtualną funkcją składową.

 
#include <iostream.h>

class instrument {

public:

void

virtual

wydaj_dzwiek()

{
cout<<”nieokreslony pisk!\n”;
}
};
//

background image

class trabka: public instrument {

public:

void wydaj_dzwiek()

{

cout<<”tra-ta-ta-ta\n”;

}

};

class beben: public instrument {

public:

void wydaj_dzwiek()

{

cout<<”bum-bum-bum\n”;

}

};

class fortepian: public instrument {

public:

void wydaj_dzwiek()

{

cout<<”plim-plim-plim\n”;

}

};

void muzyk(instrument &instrument);

background image

main()
{
instrument jakis_instrument;
trabka zlota_trabka;
fortepian steinway;
beben beben_dobosza;
 

cout<<“wywolanie funkcji skladowych na rzecz

obiektow\n“;
 
}
jakis_instrument.wydaj_dzwiek();
zlota_trabka.wydaj_dzwiek();
steinway.wydaj_dzwiek();
beben_dobosza.wydaj_dzwiek();
 ///// powinien zadzialac mechanizm przeslaniania

cout<<”wywolanie funkcji na rzecz obiektu\n

pokazanego wskaznikiem instrumentu\n”;
instrument *wskinstr;

//deklaracja wskaznika

 

background image

//ustawianie wskaznika
 
wskinstr=&jakis_instrument;

//przypisanie pod wskaźnik

referencji na obiekt
wskinstr-> wydaj_dzwiek(); // wywołanie funkcji na rzecz zawartosci
wyłuskiwanej spod wskaźnika
 
cout<<”okazuje sie ze możemy pokazac także na obiekty klasy
pochodnej”;
 
wskinstr=& zlota_trabka;
wskinstr-> wydaj_dzwiek();
 
wskinstr=& steinway;
wskinstr-> wydaj_dzwiek();
 
wskinstr=& beben_dobosza;
wskinstr-> wydaj_dzwiek();
 

background image

cout<<”albo na referencje do funkcji”;
 
 
muzyk(jakis_instrument);

//obiekt jest tu abstarkcyjna zmienna

muzyk(zlota_trabka);
muzyk(steinway);
muzyk(beben_dobosza);
}
/////
void muzyk(instrument &pysk);
{
pysk.wydaj_dźwięk();
}

background image

Po uruchomieniu programu trzymamy na ekranie:
 
wywolanie funkcji skladowych na rzecz obiektow
 
nieokreslony pisk!
tra-ta-ta-ta
bum-bum-bum
plim-plim-plim
 
wywolanie funkcji na rzecz obiektu
pokazanego wskaznikiem instrumentu
 
nieokreslony pisk!
 

background image

okazuje sie ze możemy pokazac także na obiekty klasy
pochodnej
 
tra-ta-ta-ta
bum-bum-bum
plim-plim-plim
 
albo na referencje do funkcji
nieokreslony pisk!
tra-ta-ta-ta
bum-bum-bum
plim-plim-plim
 

background image

Gdyby jednak usunąć słowo virtual przy funkcji
wydaj_dzwiek w klasie podstawowej, to na ekranie pojawi
się następujący wynik:
 
wywolanie funkcji skladowych na rzecz obiektow
 
nieokreslony pisk!
tra-ta-ta-ta
bum-bum-bum
plim-plim-plim
 
wywolanie funkcji na rzecz obiektu
pokazanego wskaznikiem instrumentu
 
nieokreslony pisk!
 

background image

okazuje sie ze możemy pokazac także na obiekty klasy
pochodnej
 
nieokreslony pisk!
nieokreslony pisk!
nieokreslony pisk!
 
albo na referencje do funkcji
nieokreslony pisk!
nieokreslony pisk!
nieokreslony pisk!
nieokreslony pisk!

background image

Czyli po wywołaniu funkcji wydaj_dźwięk na rzecz
obiektów z poszczególnych klas wykonała się po prostu
funkcja z każdej z tych klas zgodnie z zaleceniem:
 

obiekt.wydaj_dźwięk();

 Tu działał ukryty wskaźnik this oraz mechanizm
przesłaniania.
Dalej wprowadziliśmy definiowany wskaźnik, który
pokazywał na obiekty klasy instrument. Przy tym
pokazuje na jakis_instrument czyli dowolny obiekt klasy
instrument.
Następnie kierujemy wskaźnik na funkcję, co powoduje,
ze jest ona wykonana na rzecz wskazanego wcześniej
obiektu:
 

wskaźnik->wydaj_dźwięk();

 
Potem ustawiliśmy wskaźnik na obiekty klas pochodnych.
Mogliśmy to zrobić bo przy dziedziczeniu następuje
konwersja typów obiektu
i wskaźnikiem do obiektu
klasy podstawowej możemy pokazać na obiekt klasy
pochodnej.

background image

Wprawdzie typ wskaźnika jest przy dziedziczeniu ogólnie
różny od typu obiektu ale konwersja działa w ramach
mechanizmu dziedziczenia. Dlaczego jednak kompilator
wybiera właściwą obiektowi funkcję mimo takiej samej
nazwy funkcji? Sprawcą takiego zachowania kompilatora
jest słowo virtual przy funkcji składowej klasy
podstawowej.

To

ono

sprawia,

że

konwersja

przekierowuje kompilator inteligentnie także do funkcji
dla obiektu pokazanego wskaźnikiem.
Gdy słowo virtual zostało usunięte to mechanizm
prawidłowego wykonania funkcji przypisanej obiektowi
nie zadziałał i wykonywała się funkcja tylko z klasy
podstawowej.

background image

Kompilatory języków niezorientowanych
obiektowo używają tzw. wczesnego wiązania
funkcji. Kompilator generuje wywołanie funkcji a
linker zamienia to wywołanie na bezwzględny
adres kodu, który ma być wykonany.
Kompilatory w językach obiektowych stosują tzw.
późne wiązanie. Kod przy takim wiązaniu jest
wywoływany dopiero podczas wykonywania.
Kompilator tylko sprawdza poprawność i
obecność poszczególnych składników w
wiązaniu. W języku C++ takie wywołanie
powoduje słowo kluczowe virtual.

background image

Polimorfizm

Dzięki terminowi virtual fragment kodu funkcji muzyk
podany w formie
 

&wydaj_dźwięk();

 

wykonuje się w formie stosownej do zakresu klasy, z
której wskazujemy adresem obiekt:
 
&instrument::wydaj_dźwięk()
&trabka::wydaj_dźwięk()
&fortepian::wydaj_dźwięk()
 
zależnie od sytuacji. Czyli funkcja muzyk wykonała się
różnie mimo tej samej formy. To się nazywa
polimorfizmem, co oznacza wielość form. Zastosowanie
funkcji wirtualnej pozwoliło na uzyskanie wielości form.

background image

Dodatkową cechą klasy zawierającej składową funkcję

wirtualną jest to, że

zadziała uniwersalnie dla każdej

klasy pochodnej

wywołującej funkcje wydaj_dźwięk(): 

#include”instrum.h” // nasze defincje do klasy

instrument zawrzemy w pliku head

/////
class sluchacz:public instrument{
public:

void wydaj_dzwiek();

{

cout<<”jazz-jazz”;

}
////
main()
{
sluchacz bzzzzz;
muzyk(bzzzzz);
}

background image

to na ekranie otrzymamy:
 

jazz-jazz

Dlaczego? Dlatego, że instrukcja z funkcji muzyk ma
teraz formę:
 

instrument.sluchacz::wydaj_dźwięk();

 

Nietrudno zauważyć, że daje to zupełnie nowe możliwości
modyfikacji działania programu w ramach polimorfizmu.
Dlaczego w takim razie nie uznać wszystkich funkcji jako
wirtualnych w trybie domyślnym? Głównie dlatego, że
funkcje wirtualne zabierają znacznie więcej miejsca w
pamięci niż zwykłe funkcje składowe i ich uruchamianie
trwa znacząco dłużej.

background image

Należy pamiętać, że:

•wirtualna może być tylko funkcja składowa, a nie
funkcja globalna;

•słowo virtual występuje tylko przy deklaracji funkcji w
klasie, a ciało funkcji już nie musi go zawierać;

•jeśli klasa pochodna nie zdefiniuje swojej wersji funkcji
wirtualnej, to będzie ona wywoływana z klasy
podstawowej w jej zakresie ważności;

•funkcja wirtualna nie może być funkcją typu static bo
wtedy nie może być stosowana wirtualnie na wielu
obiektach a tylko na tym, na którym jest przypisana jako
static;

•funkcja wirtualna może być funkcją zaprzyjaźnioną ale
straci wówczas możliwość polimorficznego działania czyli
możemy ja zaprzyjaźnić ale za ceną utraty polimorfizmu


Document Outline


Wyszukiwarka

Podobne podstrony:
08.Klasy i funkcje wirtualne (4) , KLASY I FUNKCJE WIRTUALNE
08.Klasy i funkcje wirtualne (2) , KLASY I FUNKCJE WIRTUALNE
funkcje wirtualne labora
Polimorfizm i metody wirtualne
PROBLEMY FUNKCJONOWANIA ZESPOŁÓW WIRTUALNYCH, Socjologia i Psychologia
Wirtualizacja funkcji personalnych
socjologia Zagrożenia wynikające z funkcjonowania człowieka w wirtualnym świecie i w cyfrowym społe
W7 Białka struktura i funkcja
Odmienne ujecie funkcji motywacji oraz kontroli w organizacji wirtualnej
Polimorfizm i metody wirtualne
PROBLEMY FUNKCJONOWANIA ZESPOŁÓW WIRTUALNYCH, Socjologia i Psychologia

więcej podobnych podstron