Dědění, volání konstruktorů a destruktorů, virtuální a nevirtuální metody
Vytvořte program pro grafické objekty Bod (třída CPoint:
souřadnice x a y), Úsečka (CLine: dva krajní body), Kříž (CCross: dvě
úsečky). Všechny zděděné třídy budou mít společné rozhraní dané
třídou CBase. Třída CBase bude obsahovat metody:
- konstruktor
-
destruktor (zjistěte rozdíly mezi virtuálním a nevirtuálním)
-
čistě virtuální metodu Print – ve zděděné třídě vytiskne jméno
třídy pomocí typeid.name
- nevirtuální metodu PrintN.
Všechny
děděné třídy přetíží všechny metody.
Všechny metody vytisknou svůj
název a třídu ke které patří. Npař. „CBase::Print “.
Pomocí těchto tisků se zjistí jak jsou metody volány.
1) Vytvořte bázovou třídu CBase, která bude mít metody: implicitní
konstruktor, destruktor, Print, PrintN.
Metody budou tisknout své
jméno.
2) Vytvořte odvozenou třídu CPoint od CBase pomocí public dědění, obsahující double souřadnice x a y. Metody bude mít jako bázová třída.
3) Vytvořte odvozenou třídu CLine od CBase pomocí public dědění, obsahující dva body CPoint. Metody bude mít jako bázová třída.
4) Vytvořte odvozenou třídu CLine od CBase pomocí public dědění, obsahující dvě úsečky CLine. Metody bude mít jako bázová třída.
5) Vytvořte v main jeden objekt od každé třídy (aa,bb,cc).
Spusťte a podívejte se co se tiskne - kudy prochází program.
6) Zavolejte pro všechny objekty (aa,bb,cc) jejich metodu Print a
PrintN.
Spusťte a podívejte se co se tiskne - kudy prochází
program.
7) Prostudujte si sekci s dynamicky vytvořeným prvkem CCross
uloženým do ukazatele na CPoint.
Vytvořte dynamickou proměnnou
typu CCross a uložte ji do ukazatele na jinou třídu. Ihned ji zrušte
pomocí delete. (Tento bod není programátorsky korektní ale u našich
tříd bude fungovat).
8) Vytvořte v main pole pro tři ukazatele pb,pd1,pd2 na každou třídu (typ třídy) a nainicializujte je pomocí vytvořených prvků (všechny stejně => vzniknou všechny kombinace tříd ukazatelů a objektů).
CBase *Elements[3] =
{&aa,&bb,&cc};
CLine *Lines[3] = {(CLine *) &aa,(
CLine *)&bb,( CLine *)&cc };
CCross *Crosses[3] = {
(CCross *)&bb,(CCross *)&bb,(CCross *)&cc };
(Druhý
a třetí řádek nejsou programátorsky korektní ale u našich tříd budou
fungovat).
9) pro ukazatele z minulého bodu zavolejte metodu PrintN. Co očekáváte a co se stane.
10) Nyní zavolejte metodu Print, která je realizována pozdní
vazbou (polymorfizmus).
Porovnejte tisk průběhu s minulým tiskem
pro časnou vazbu pomocí PrintN.
Na jakých proměnných se projeví
změna tisku (objekt nebo přístup přes ukazatel)?
11) Vysvětlete, která/které kombinace ukazatelů pb,pd1,pd2 a jejich inicializací dávají smysl, a které jsou špatně.
12) Je možné zjistit typ proměnné "programově" (všimněte
si, že plně "funguje" až po zavedení vlastnosti virtual,
kdy použije pozdní vazbu. Jinak je tato funkce "vyřešena"
při překladu).
Pomocí příkazu typeid je možné získat strukturu
type_info.
Pomocí metody name() můžeme například zjistit jméno
typu objektu. typeid( XXX ).name();
13) Přidejte/uberte u destruktoru virtualitu a srovnejte jednotlivé průběhy.
int main()
{
{ // Sledujte postup volání konstruktorů
// CBase base; // proč hlásí chybu?
cout << "end base ============== " << endl;
CPoint aa; // bod obsahuje polohu souřadnic x a y
cout << "end CPoint ============== " << endl;
CLine bb; // úsečka je dána dvěma krajními body, obsahuje dva parametry typu CBod
cout << "end CLine ==============" << endl;
CCross cc; // kříz ze dvou úseček, obsahuje dva parametry typu CLine
cout << "end CCross ============== " << endl;
#if 0
// co se bude tisknout?
aa.Print(); aa.PrintN();
bb.Print(); bb.PrintN();
cc.Print(); cc.PrintN();
cout << "start dynamicka alokace ============== " << endl;
// následující je programátorsky spatně (proč?) ale dává (mozná lepsí) představu o
// rozdílu mezi metodou a virtuální metodou, nez předchozí korektní příklady
CPoint *bod = (CPoint*)new CCross;
// které metody se zavolají?
bod->PrintN(); // co vidí překladač
bod->Print(); // jak objekt vznikl
// který destruktor?
delete bod;
cout << "end dynamicka alokace ============== " << endl;
int i;
CBase* Elements[] = { &aa, &bb,&cc };
CPoint *Points[3] = { (CPoint *)&aa,(CPoint *)&bb,(CPoint *)&cc };
CLine *Lines[3] = { (CLine *)&aa,(CLine *)&bb,(CLine *)&cc };
CCross *Crosses[3] = { (CCross *)&aa,(CCross *)&bb,(CCross *)&cc };
// co vytisknou nasledujici cykly?
for (i = 0; i < 3; ++i) {
Elements[i]->PrintN();
Points[i]->PrintN();
Lines[i]->PrintN();
Crosses[i]->PrintN();
}
for (i = 0; i < 3; ++i) {
Elements[i]->Print();
Points[i]->Print();
Lines[i]->Print();
Crosses[i]->Print();
}
#endif
cout << "end Program ============== " << endl;
} // sledujte postup volání destruktorů
return 0;
}
Priklad vystupu: tisk meho retezce < tisk typeid >
CBase:implicit c'tor < class CBase >
CPoint:implicit c'tor < class CPoint >
CLine:implicit c'tor < class CLine >
CCross:implicit c'tor < class CCross >
end CCross ==============
end Program ==============
CCross:d'tor < class CCross >
CLine:d'tor < class CLine >
CPoint:d'tor < class CPoint >
CBase:d'tor < class CBase >