Stanovení volání konstruktorů a volání virtuálních metod
znění příkladu (vzorový příklad pro demonstraci probírané látky, možností kombinací takovýchto příkladů jsou prakticky nevyčerpatelné – variace na takovýto příklad se může vyskytnout u zkoušky):
#include <iostream> using std::cout;
class A { public: A(void) {cout << 'a';} A(int) {cout << 'n';} virtual ~A(void) {cout << 'b';} void f(void) {cout << 'c';} virtual void fv(void) {cout << 'd';} };
class B:public A { A a; public: B(void) {cout << 'e';} B(int) {cout<< 'm';} virtual ~B(void) {cout << 'f';} void f(void) {cout << 'g';fv();} virtual void fv(void) {cout << 'h';} };
class C:public A { B a,c; A b; public: C(void) :b(2),c(1),a(){cout << 'i';} virtual ~C(void) {cout << 'j';} void f(void) {cout << 'k';fv();} };
int main () { A *a; B b; A *c = (A*)new C; a = &b; a -> f(); // nevirtuální -> volání podle definice -> f z A a -> fv(); // virtuální -> volání podle vzniku -> fv z B (a ukazuje na b, to vzniklo B) c -> f(); // nevirtuální -> volání podle definice -> f z A c -> fv(); // virtuální -> volání podle vzniku -> fv z C (c ukazuje na A, ale vzniklo jako C) delete c; b.f(); // nevirtuální -> volání podle definice -> f z B b.fv(); // virtuální -> volání podle vzniku -> fv z B (b je typu B a vzniklo jako B) }
|
Diagram závislostí tříd daného příkladu
|
Výsledný tisk: aae aaaeaamni c h c d jbfbbfbbb ghh fbb (konstruktory, volání metod, destruktor, volání metod, destruktor).
Zkuste vymazat virtual u destruktorů a všimněte si změny.
Pravidla pro „řešení“
jsou shrnuta na konci přednášek denního studia (asi tohle:
Pravidla
pro volání konstruktorů a
destruktorů (automatické
(definice objektů) i
dynamické proměnné (vytvoření
pomocí new))
1) volání konstruktorů (v
každém kroku se začíná od a),
pokud byl bod na dané úrovni vyřešen,
pokračuje se dalším)
a)
konstruktor bázové třídy
(existuje-li bázová třída, a
zde opět od a))
b)
konstruktory objektů třídy
(existují-li, v pořadí daném
definicí objektů ve třídě.
Pro každý objekt se provádí a) b) c) ). Předepsané
konstruktory v hlavičce
konstruktoru neurčují pořadí
ale typ.
c) vlastní tělo
konstruktoru
2) volání destruktorů –
je v opačném
pořadí jako volání
konstruktorů. Mohou být
virtuální (potom se volají v opačném
pořadí v jakém došlo ke
konstrukci, nehledě na to,
čemu je objekt v současnosti
přiřazen).
Pokud jsou nevirtuální, jsou destruktory volány v opačném
pořadí ke konstruktorům,
jaké by se volaly pro prvek třídy,
které je objekt právě přiřazen.
Destruktor je volán na konci definičního
bloku pro proměnné v něm
definované. Destruktor je volán při
delete.
Pravidla pro
volání (virtuálních) metod
1) zjistíme, zda-li je volaná metoda
virtuální nebo nevirtuální
2) pokud je volaná metoda nevirtuální,
rozhoduje “jak to vidí” překladač – neboli je
volána taková metoda, která patří k typu (třídě) jak je současný
objekt (ukazatel na objekt) definován.
3) pokud je volaná metoda
virtuální, nerozhoduje, čemu je aktuálně prvek přiřazen, ale jak “se
narodil”. Při vzniku (konstruktor) je mu totiž virtuální metoda
přiřazena na základě vznikajícího typu (a zůstává “majetkem”
objektu). U objektu se při běžné činnosti nejedná o problém, protože
je známo jak objekt vznikl (podle typu v definici). Důležité je to
však u ukazatelů, které mohou ukazovat na cokoli (i když z hlediska
daného mechanizmu je nutné dodržet to, že přiřazovat by se měl
ukazatel na potomka do ukazatele na předka). Potom musíme najít, jak
opravdu vznikl objekt (definice, nebo new), na který se právě ukazuje
– nezávisle na množství přiřazení, které se staly.
4) Pokud
metoda neexistuje, použije se metoda nejbližší (například u předka).
Poslední změna 2009-12-29