Programování v C++ VI
Konstruktory, destruktory a dědičnost
Peta (
[email protected], SPR AG 2008-9)
Konstruktory a dědičnost I ●
●
když jsme se bavili o dědičnosti, trochu jsme zapomněli na konstruktory to se ale nevyplácí, vzpomeňte si, jak důležitý je konstruktor ●
●
●
konstruktor se volá vždy při inicializaci objektu a slouží k uvedení členských proměnných do rozumného stavu konstruktor také často alokuje zdroje objektu (paměť, atd.)
je dokonce tak důležitý, že pokud ho vynecháme, přidá překladač automaticky konstruktor defaultní ●
a ten nemá žádné parametry a ani nic nedělá (alespoň si to zatím myslíme...) Peta (
[email protected], SPR AG 2008-9)
Konstruktory a dědičnost II ●
●
pokud je třída potomkem jiné třídy (jiných tříd), počítá každá její funkce s tím, že veškerá funkčnost rodičů je jí již k dispozici a konstruktor není vyjímkou, i konstruktor potomka očekáva, že předek již existuje. ●
●
potomek a předek jsou sice defacto jeden a ten samý objekt, to, že předek již existuje můžeme chápat spíš tak, že konstruktor potomka očekává, že ta část, odpovídající funkčnosti předka je již ve správném a smysluplném stavu
tedy, že již konstruktor předka proběhl ●
ještě před vstupem do těla konstruktoru potomka!! Peta (
[email protected], SPR AG 2008-9)
Konstruktory a dědičnost III ●
pokud existuje defaultní konstruktor (tedy konstruktor bez argumentů), může ho překladač zavolat sám ●
●
proto zatím naše příklady fungovaly, defaultní konstruktory volal automaticky překladač
a právě proto překladač automaticky dodává implicitní defaultní konstruktor, i když ho nedefinujeme ●
●
dělá to, aby měl místo, na kterém může konstruktory předků volat i když defaultní konstruktor v potomkovi nemusí nic dělat, vždy volá konstruktory předků (pokud existují) Peta (
[email protected], SPR AG 2008-9)
Konstruktory a dědičnost IV ●
●
●
co ale dělat, když nemáme defaultní konstruktor k dispozici? pak už překladač nemůže konstruktor zavolat, protože neví, jak inicializovat jeho argumenty o zavolání konstruktorů předků se tedy musíme postarat my ● ●
●
a musíme to udělat ještě před vstupem do těla funkce jediná možná cesta je tedy napsat volání konstruktorů předků za seznam argumentů konstruktoru a před otevírací závorku volání konstruktorů předků ale nepatří do hlavičky funkce a proto se zapisují až do implementačního souboru (pokud konstruktor není inlinován, pak je v hlavičce i definice) Peta (
[email protected], SPR AG 2008-9)
Konstruktory a dědičnost V class A { public: A(int i) { cout << “Konstruktor A:” << i << endl } }; class B:public A { public: B():A(5) { } // Defaultni konstruktor B je ted nutne napsat B(int i):A(i+2) { cout << “Konstruktor B” << I << endl; } } Peta (
[email protected], SPR AG 2008-9)
Konstruktory a dědičnost VI ●
●
protože třída A nemá defaultní konstruktor, třída B nemůže mít konstruktor implicitní ●
protože překladač neví jak volat parametrizovaný konstruktor A
●
musíme ho zavolat sami, což dělá první konstruktor v B
druhý konstruktor pak má parametr a volá konstruktor A s tímto upraveným parametrem ●
parametry pro konstruktor předka totiž můžete upravit v rámci jeho volání výrazy (I voláním funkcí (ne však vlastních, metod, ty ještě nejsou dostupné)
Peta (
[email protected], SPR AG 2008-9)
Virtuální Destruktory I ●
v souvislosti s dědičností jsme se taky nebavili vůbec o destruktorech ●
●
destruktory jsou stejně důležité jako konstruktory ●
●
●
což je taky chyba...
starají se o uvolnění zdrojů objektu, v C++ typicky paměti
stejně jako se musí zavolat před konstruktorem potomka všechny konstruktory předků, je situace u destruktoru obdobná všechno je ale obráceně – nejdřív se zničí potomek a pak se zničí všichni jeho předkové ●
obojí rekurzivně ;-) Peta (
[email protected], SPR AG 2008-9)
Virtuální destruktory II ●
●
problém ale nastane, když používáme polymorfismus pokud totiž destruktor není virtuální, zavolání destruktoru polymorfního předka nezpůsobí zavolání destruktoru v potomkovi ●
●
což vede k segfaultům a dalším méně příjemným věcem, kterých se všichni snažíme vyvarovat
zásada by opět měla být: pokud si nejste jisti, že nebudete potřebovat polymorfismus pro destruktory, mějte destruktor vždy virtuální Peta (
[email protected], SPR AG 2008-9)
Virtuální destruktory III class A { public: virtual ~A() { cout << “killing A” << endl; } }; class B:public A { public: virtual ~B() { cout << “killing B” << endl; } }; void killObject(A* x) { delete x; } killObject(new B()); Peta (
[email protected], SPR AG 2008-9)
Konstruktory a destruktory ●
●
všimněte si, že překladač automaticky rekurzivně volá konstruktory a destruktory předchůdců toto chování je specifické pouze pro konstruktory a destruktory ●
●
jen tady je totiž nezbytně nutné
všude jinde si je musíte vynutit:
void overridenMethod() { // do my stuff Parent::overridenMethod(); } Peta (
[email protected], SPR AG 2008-9)
A zase je tu konec...
dneska rychle...
Peta (
[email protected], SPR AG 2008-9)