Treść książki

Przejdź do opcji czytnikaPrzejdź do nawigacjiPrzejdź do informacjiPrzejdź do stopki
żeprawdopodobniemamydoczynieniazkodemobiektowymafunkcjewywoływanewtensposóbmetodamipewnejklasy.
Naturalnymwydawaćbysięmogło,żemetodyklasybędąnaetapiekompilacjizapisywanetaksamojakelementystrukturyjako
wskaźnikinafunkcje.Jaksięjednakokazujeniematakiejpotrzeby.Przedewszystkimdlatego,żekompilatorpodczasprzetwarzaniakodu
dopostacibinarnejdobrze„wie”,dojakiejklasynależyobiektijakąfunkcjętrzebawywołaćjakojegometodę(pamiętająco
this
wrejestrzeECX).
Niezawszejednak…
1.3.3.Wszystkozostajewrodzinie,czylidziedziczenie
Istniejejeszczejedenprzypadekwymagającyrozważeniaamianowiciedziedziczenieorazmetodywirtualne.Problemdobrzeilustruje
kodpokazanywlistingu1.11.
#include<cstdio>
classBase{
intprop;
public:
virtualvoidfunc1(void){puts("->F1(base)");}
virtualvoidfunc2(void){puts("->F2(base)");}
Base(){puts("NEW:Base");}
virtual~Base(){puts("DEL:Base");}
};
classChild1:publicBase{
public:
Child1(){puts("NEW:Child1");}
virtualvoidfunc1(void){puts("->F1(child)");}
};
classChild2:publicBase{
public:
virtualvoidfunc2(void){puts("->F2(child)");}
};
voiddo_stuff(Base*o){
o->func1();
o->func2();
deleteo;
}
voidwork(void){
puts("--object1--");
do_stuff(newChild1());
puts("--object2--");
do_stuff(newChild2());
}
intmain(void){
work();
return0;
}
Listing1.11.Kodprogramudemonstrującegodziedziczenieorazmetodywirtualne
KlasagłównaBasemadwiemetody:func1orazfunc2.KażdazklaspotomnychChild1orazChild2implementuje(przeciąża)jedną
znich.
Wtejsytuacjifunkcjado_stuffprzyjmujewskaźniknaobiektklasy„matki”(Base),anastępniewywołujejegodwiemetodyiniszczygo.
JakmożnasięprzekonaćpomimoprzekazaniareferencjidoobiektuklasyBasewykonywanyjestkodprzeciążonychfunkcji.Kompilator
naetapiebudowaniakodubinarnegoniewie,jakiobiektzostanieprzekazanyjakoargumentfunkcjido_stuffatymsamymniewie,które
konkretniezmetodpowinnyzostaćwywołane.Patrzącnalisting1.12,widzimy,programdziałazgodniezoczekiwaniami.Zatemwjakiś
tajemniczysposóbprzekazywanajestinformacja(zapewnezapomocąwskaźnikajaktobywawkodziemaszynowym)ofunkcji,która
mazostaćwywołana.
>g++inh.cpp-oinh-m32
>./inh.exe
--object1--
NEW:Base
NEW:Child1
->F1(child)
->F2(base)
DEL:Base
--object2--
NEW:Base
->F1(base)
->F2(child)
DEL:Base
Listing1.12.Kompilacjaorazuruchomienieprogramuprezentującegodziedziczenie
Przyjrzyjmysiębliżejdeasemblacjifunkcjiprzedstawionejnarysunku1.20.Ramkamizaznaczonowywołaniafunc1orazfunc2.Tużprzed
wywołaniemdorejestruECXprzypisywanajestwartośćpierwszego(jedynego)parametrufunkcjido_stuff.Widaćrównieżpewneoperacje
wykonywaneprzedsamąinstrukcjąCALL„tajemnicze”odnajdywaniewskaźnikadofunkcji,którąnależywywołać.Analizując
poszczególneetapypokoleiwidzimy,że:
1.DorejestruEAXprzypisywanajestwartośćarg_0.
2.DorejestruEAXprzypisywanajestwartośćwskazywanaprzezrejestrEAX(dwukrotnie).
3.Adres,naktórywskazujerejestrEAX,zostajewywołanyjakofunkcja.