Download presentation
Presentation is loading. Please wait.
1
Polymorfizmus
2
Osnova prednášky Preťažovanie funkcií Použitie štandardných argumentov
Kopírovací konštruktor Operátory a ich preťažovanie
3
Polymorfizmus - mnohotvárnosť
Ide o viacúčelové využitie funkcií alebo operátorov Metóda s jedným názvom môže byť použitá pre rôzne typy dát, rôzny počet vstupov alebo rôzne návratové hodnoty, čo uľahčuje orientáciu v programe
4
Preťažovanie funkcií Ide o situáciu, keď v programe definujeme viacero funkcií s tým istým názvom, ale s rôznymi vstupmi alebo výstupmi. V programe to teda vyzerá takto: typ1 Funkcia(parametre1); typ2 Funkcia(parametre2); typ3 Funkcia(parametre3); ... Podľa situácie sa prekladač sám vie rozhodnúť, ktorý tvar funkcie mal programátor na mysli – jednotlivé verzie sa musia líšiť počtom alebo typom parametrov. Preťažovanie môžeme využiť nielen v objektovom, ale aj v procedurálnom programovaní Mlynček(na mäso) Mlynček(na mak) Mlynček(na orechy)
5
Preťažovanie funkcií Príklad: Počítanie absolútnych hodnôt čísel pomocou procedurálneho programovania Vytvoríme funkciu absolut, ktorá bude počítať absolútnu hodnotu celého alebo reálneho čísla. int absolut(int x) { if (x>0) return x; else return -x; } float absolut(float x)
6
Preťažovanie funkcií V hlavnom programe potom funkcie použijeme:
int main() { int a=-1; float b=2.3; cout<<"Absolutna hodnota cisla "<<a<<" je "<<absolut(a)<<endl; cout<<"Absolutna hodnota cisla "<<b<<" je "<<absolut(b)<<endl; return 0; } Dostaneme výstup: Absolutna hodnota cisla -1 je 1 Absolutna hodnota cisla 2.3 je 2.3
7
Preťažovanie funkcií Príklad: Počítanie absolútnych hodnôt čísel pomocou OOP Vytvoríme triedu Kalkulačka, ktorá bude počítať absolútne hodnoty z celých, reálnych a komplexných čísel. Keďže budeme počítať aj s komplexnými číslami, vytvoríme si najprv triedu KomplexnéČíslo: class KomplexneCislo { private: float a, b; public: KomplexneCislo(float a, float b) {this->a=a; this->b=b;} float A() {return a;} float B() {return b;} };
8
Preťažovanie funkcií Teraz definujeme triedu Kalkulačka:
class Kalkulacka { public: int abs(int x); float abs(float x); float abs(KomplexneCislo x); }; int Kalkulacka::abs(int x) if (x>0) return x; else return -x; }
9
Preťažovanie funkcií Varianty funkcie pre reálne a komplexné čísla:
float Kalkulacka::abs(float x) { if (x>0) return x; else return -x; } float Kalkulacka::abs(KomplexneCislo x) return sqrt(x.A()*x.A()+x.B()*x.B());
10
Preťažovanie funkcií V hlavnom programe vytvoríme objekty a vypočítame absolútne hodnoty: int main() { int n=-3; float f=2.5; KomplexneCislo c(3,4); Kalkulacka K; cout<<"Absolutna hodnota cisla "<<n<<" je "<<K.abs(n)<<endl; cout<<"Absolutna hodnota cisla "<<f<<" je "<<K.abs(f)<<endl; cout<<"Absolutna hodnota cisla "<<c.A()<<"+"<<c.B()<<"i je " <<K.abs(c)<<endl; return 0; } Dostaneme výstup: Absolutna hodnota cisla -3 je 3 Absolutna hodnota cisla 2.5 je 2.5 Absolutna hodnota cisla 3+4i je 5
11
Použitie štandardných argumentov
Príklad: Kalkulačka, ktorá počíta obsah obdĺžnika Do triedy Kalkulačka doplníme ďalšiu funkciu – výpočet obsahu obdĺžnika pre zadané strany a,b. Keďže obdĺžnik môže byť v skutočnosti aj štvorec, definujeme aj verziu funkcie, ktorá bude mať len jeden parameter – dĺžku strany a. Nová trieda Kalkulačka bude vyzerať takto: class Kalkulacka { public: int abs(int x); float abs(float x); float abs(KomplexneCislo x); float obsah(float a, float b) {return a*b;} float obsah(float a) {return a*a;} };
12
Použitie štandardných argumentov
V hlavnom programe použijeme nové funkcie: int main() { float a=5.2, b=3.4; Kalkulacka K; cout<<"Obsah obdlznika so stranami "<<a<<", "<<b<<" je: " <<K.obsah(a,b)<<endl; cout<<"Obsah stvorca so stranou "<<a<<" je: " <<K.obsah(a)<<endl; return 0; } Výstup: Obsah obdlznika so stranami 5.2, 3.4 je: 17.68 Obsah stvorca so stranou 5.2 je: 27.04
13
Použitie štandardných argumentov
Existuje aj iný spôsob, ako vyriešiť uvedený problém. Namiesto dvoch funkcií definujeme len jednu, so štandardnou hodnotou jedného argumentu. Bude ním argument b, ak jeho hodnota bude nulová, znamená to, že ide o štvorec. Trieda Kalkulačka bude definovaná takto: class Kalkulacka { public: int abs(int x); float abs(float x); float abs(KomplexneCislo x); float obsah(float a, float b=0) {if (b==0) return a*a; else return a*b;} }; Hlavný program aj výstup bude rovnaký ako v predchádzajúcom prípade. Ak zavoláme funkciu len s jedným argumentom, b bude mať automaticky hodnotu 0 a funkcia spozná, že má vypočítať obsah štvorca.
14
Použitie štandardných argumentov
Štandardnú hodnotu môžu mať všetky argumenty. Upravme teraz funkciu tak, aby vrátila obsah jednotkového štvorca, ak je volaná bez argumentov. Nová definícia triedy Kalkulačka teda bude: class Kalkulacka { public: int abs(int x); float abs(float x); float abs(KomplexneCislo x); float obsah(float a=0, float b=0) {if (a==0 && b==0) return 1; if (b==0) return a*a; else return a*b;} };
15
Použitie štandardných argumentov
V hlavnom programe potom použijeme funkciu obsah všetkými tromi spôsobmi: int main(int argc, char* argv[]) { float a=5.2, b=3.4; Kalkulacka K; cout<<"Obsah obdlznika so stranami "<<a<<", "<<b<<" je: "<<K.obsah(a,b)<<endl; cout<<"Obsah stvorca so stranou "<<a<<" je: "<<K.obsah(a)<<endl; cout<<"Obsah stvorca so stranou 1 je: "<<K.obsah()<<endl; return 0; } Výstup: Obsah obdlznika so stranami 5.2, 3.4 je: 17.68 Obsah stvorca so stranou 5.2 je: 27.04 Obsah stvorca so stranou 1 je: 1
16
Použitie štandardných argumentov
Príklad: Trieda Kalkulačka, v ktorej funkcia obsah nie je inline class Kalkulacka { public: int abs(int x); float abs(float x); float abs(KomplexneCislo x); float obsah(float a=0, float b=0); }; float Kalkulacka::obsah(float a=0, float b=0) if (a==0 && b==0) return 1; if (b==0) return a*a; else return a*b; }
17
Použitie štandardných argumentov
Pri kompilácii takéhoto kódu kompilátor vyhlási chybu: Problém je v tom, že štandardné hodnoty argumentov nemôžu byť dané v deklarácii aj definícii funkcie. Musia byť dané len raz, nezáleží na tom, či v deklarácii alebo definícii.
18
Použitie štandardných argumentov
Ďalším pravidlom je, že parametre, ktoré majú danú štandardnú hodnotu, musia v hlavičke nasledovať až za tými, ktoré štandardnú hodnotu nemajú. Je teda neprípustná takáto hlavička: float obsah(float a=0, float b); Ďalej, ak máme dané štandardné hodnoty: float obsah(float a=0, float b=0); a zavoláme funkciu: obsah(2.0) hodnota 2.0 je priradená parametru a. Neexistuje spôsob, ako priradiť hodnotu len parametru b.
19
Preťažovanie a nejednoznačnosti
Príklad: Absolútna hodnota čísla typu double Do triedy Kalkulačka doplníme ďalší variant funkcie abs, ktorý bude počítať absolútnu hodnotu čísel typu double. class Kalkulacka { public: int abs(int x); float abs(float x); double abs(double x); float abs(KomplexneCislo x); float obsah(float a=0, float b=0); }; Trochu pozmeníme aj definície metód: int Kalkulacka::abs(int x) cout<<"Pocitam absolutnu hodnotu cisla typu int"<<endl; if (x>0) return x; else return -x; }
20
Preťažovanie a nejednoznačnosti
float Kalkulacka::abs(float x) { cout<<"Pocitam absolutnu hodnotu cisla typu float"<<endl; if (x>0) return x; else return -x; } double Kalkulacka::abs(double x) cout<<"Pocitam absolutnu hodnotu cisla typu double"<<endl; V hlavnom programe potom zavoláme funkcie takto: Kalkulacka K; cout<<"Absolutna hodnota cisla 10 je "<<K.abs(10)<<endl; cout<<"Absolutna hodnota cisla 10.0 je "<<K.abs(10.0)<<endl;
21
Preťažovanie a nejednoznačnosti
Ktorá verzia funkcie abs bude použitá pre čísla 10 a 10.0? Vidíme, že v prípade čísla 10 sa program rozhodol pre verziu pre typ int, v prípade čísla 10.0 použil verziu pre typ double. Máme však v programe nejednoznačnosť, a v závislosti od okolností a od kompilátora sa program ani nemusí podariť skompilovať. Mali by sme sa preto snažiť nejednoznačnosti z programu odstrániť.
22
Preťažovanie a nejednoznačnosti
Príklad: Podávanie parametra hodnotou a odkazom Majme v triede Kalkulačka ďalšiu verziu funkcie abs, ktorá nielen vráti absolútnu hodnotu čísla, ale aj ňou prepíše pôvodnú hodnotu. class Kalkulacka { public: int abs(int x); int abs(int &x); ... }; int Kalkulacka::abs(int &x) cout<<"Pocitam absolutnu hodnotu cisla typu int"<<endl; if (x<=0) x=-x; return x; }
23
Preťažovanie a nejednoznačnosti
V hlavnom programe skúsime potom zavolať funkciu abs: Kalkulacka K; cout<<"Absolutna hodnota cisla "<<n<<" je "<<endl; cout<<K.abs(n)<<endl; V tomto prípade program nemá nijakú možnosť rozhodnúť, ktorú verziu funkcie abs mal programátor na mysli. Výsledkom teda bude závažná nejednoznačnosť a program sa nepodarí skompilovať.
24
Preťažovanie a nejednoznačnosti
Príklad: Metódy so štandardnými argumentami Do triedy Kalkulačka doplníme funkciu obsah, ktorá bude počítať len obsah štvorca. class Kalkulacka { public: ... float obsah(float a=0, float b=0); float obsah(float a) {return a*a;} }; V hlavnom programe sa pokúsime funkciu obsah zavolať: double a=5.2; Kalkulacka K; cout<<"Obsah stvorca so stranou "<<a<<" je: "<<K.obsah(a)<<endl; Ani v tomto prípade kompilátor nedokáže rozlíšiť, o ktorú verziu funkcie ide, a preto vyhlási chybu.
25
Preťažovanie konštruktora
Príklad: Sčítanie komplexných čísel Do triedy KomplexnéČíslo, ktorú sme si už vytvorili, doplníme funkciu, ktorá k danému číslu pripočíta iné komplexné číslo. Budeme potrebovať aj metódy na zmenu hodnoty komplexného čísla a jeho výpis. class KomplexneCislo { private: float a, b; public: KomplexneCislo(float a, float b) {this->a=a; this->b=b;} KomplexneCislo Plus(KomplexneCislo &x); float A() {return a;} float B() {return b;} void ZmenA(float a) {this->a=a;} void ZmenB(float b) {this->b=b;} void Vypis() {if (b>=0) cout<<a<<"+"<<b<<"i"; else cout<<a<<b<<"i";} };
26
Preťažovanie konštruktora
Definujme funkciu Plus: KomplexneCislo KomplexneCislo::Plus(KomplexneCislo &x) { KomplexneCislo z(0,0); z.ZmenA(x.a+a); z.ZmenB(x.b+b); return z; } V hlavnom programe ju potom použijeme: KomplexneCislo x(1,2), y(3,-1), z(0,0); z=x.Plus(y); cout<<"Sucet cisel "; x.Vypis(); cout<<" a "; y.Vypis(); cout<<" je "; z.Vypis(); Sucet cisel 1+2i a 3-1i je 4+1i
27
Preťažovanie konštruktora
Príklad: Súčet komplexných čísel s preťažením konštruktora V predchádzajúcom príklade sme videli, že sme na viacerých miestach potrebovali pomocnú premennú typu KomplexnéČíslo. Do nej sa uložili výsledky sčítania. V našom prípade sme reálnu aj imaginárnu časť čísla nastavili na 0, ale v takomto prípade nie je potrebné, aby tieto premenné mali inicializovanú hodnotu. Je výhodné mať k dispozícii aj prázdny konštruktor: class KomplexneCislo { public: KomplexneCislo() {} KomplexneCislo(float a, float b) {this->a=a; this->b=b;} ... }; Potom môžeme objekt vytvoriť aj bez inicializácie hodnôt: KomplexneCislo z;
28
Preťažovanie konštruktora
Ďalším prípadom, keď je výhodné mať aj konštruktor bez parametrov, je vytváranie poľa objektov. Nemusíme tak inicializovať prvky poľa. Príklad: Počítanie násobkov komplexného čísla int main() { KomplexneCislo x(1,2), z[4]; for (int i=1;i<=4;i++) z[i].ZmenA(x.A()*i); z[i].ZmenB(x.B()*i); z[i].Vypis(); cout<<endl; } return 0; 1+2i 2+4i 3+6i 4+8i
29
Preťažovanie konštruktora
Ak chceme dynamicky alokovať pole objektov, je nutné mať k dispozícii aj konštruktor bez parametrov. Príklad: Počítanie vopred neznámeho počtu násobkov komplexného čísla KomplexneCislo x(1,2), *z; int n; cout<<"Kolko nasobkov sa ma vypocitat? "; cin>>n; z=new KomplexneCislo[n]; for (int i=1;i<=n;i++) { z[i].ZmenA(x.A()*i); z[i].ZmenB(x.B()*i); z[i].Vypis(); cout<<endl; } Kolko nasobkov sa ma vypocitat? 3 1+2i 2+4i 3+6i
30
Preťažovanie konštruktora
Samozrejme, trieda môže mať neobmedzený počet konštruktorov s rôznym počtom a typom parametrov. Kvôli prehľadnosti nie je však vhodné veľké množstvo rôznych konštruktorov. Príklad: Program, ktorý vypočíta, koľko dní ostáva do najbližšej výplaty. Vytvoríme triedu Dátum, pričom dátum sa bude dať inicializovať číselnými hodnotami deň, mesiac, rok alebo znakovým reťazcom (čo je výhodné pri zadávaní z klávesnice): class Datum { private: int den, mesiac, rok; public: Datum(char *s) {sscanf(s,"%i%*c%i%*c%i",&den,&mesiac,&rok);} Datum(int d, int m, int r) {den=d; mesiac=m; rok=r;} void Vypis() {cout<<den<<". "<<mesiac<<". "<<rok<<endl;} int Den() {return den;} int Mesiac() {return mesiac;} int Rok() {return rok;} };
31
Preťažovanie konštruktora
Hlavný program: int main() { int PocetDni[12]={31,28,31,30,31,30,31,31,30,31,30,31}; int d,m,r; char sdat[10]; cout<<"Aky je dnesny datum? "; cin>>sdat; Datum dat(sdat); cout<<"Ktory den v mesiaci chodi vyplata? "; cin>>d; r=dat.Rok(); if (dat.Den()<=d) m=dat.Mesiac(); ...
32
Preťažovanie konštruktora
else { if (dat.Mesiac()==12) m=1; r=dat.Rok()+1; } else m=dat.Mesiac()+1; Datum vyplata(d,m,r); cout<<"Najblizsia vyplata bude dna "; vyplata.Vypis(); if (vyplata.Mesiac()==1 && dat.Mesiac()==12) d=d+31; else d=d+PocetDni[dat.Mesiac()-1]*(vyplata.Mesiac()-dat.Mesiac()); cout<<"Do vyplaty zostava "<<d-dat.Den()<<" dni"<<endl; return 0;
33
Preťažovanie konštruktora
Výstup: Aky je dnesny datum? Ktory den v mesiaci chodi vyplata? 8 Najblizsia vyplata bude dna: Do vyplaty zostava 5 dni
34
Kopírovací konštruktor
V predchádzajúcich prednáškach sme sa stretli so situáciami, keď vytvorenie presnej kópie objektu spôsobilo problémy v programe, alebo dokonca jeho zrútenie. Ukázalo sa, že nežiadúce je najmä vytváranie presnej kópie v prípade, ak objekt dynamicky alokuje pamäť. Ak sa totiž skopíruje aj smerník, dva (alebo viac) objektov obsahuje smerníky ukazujúce na to isté miesto. To vedie k viacnásobným dealokáciám, memory leakom a podobne. Problémy nastali najmä v troch prípadoch: 1. Pri priraďovaní objektov 2. Ak objekt vystupoval ako parameter funkcie 3. Ak bol objekt návratovou hodnotou funkcie
35
Kopírovací konštruktor
V C++ existuje špeciálny typ konštruktora, ktorý sa zavolá práve v prípadoch, keď sa má vytvoriť bitová kópia objektu. Tento konštruktor zabezpečí správne kopírovanie objektov. Nazýva sa kopírovací konštruktor a volá sa v nasledujúcich prípadoch: 1. Inicializácia priradením: meno_triedy objekt1=objekt2 2. Objekt ako parameter funkcie: f(objekt) 3. Objekt ako návratová hodnota funkcie: objekt=f() Kopírovací konštruktor sa nevolá pri priradení objekt1=objekt2! (V tomto prípade nejde o vytvorenie nového objektu)
36
Kopírovací konštruktor
Príklad: Priraďovanie znakových reťazcov pomocou kopírovacieho konštruktora Na jednej z predchádzajúcich prednášok sme vytvorili triedu moj_string, ktorej súčasťou bolo dynamické alokovanie pamäte. Pri priraďovaní objektov tejto triedy vznikali problémy. Upravme teraz túto triedu tak, aby obsahovala kopírovací konštruktor. class moj_string { private: char *s; int dlzka; public: moj_string(int d, char *retazec); moj_string(moj_string &str); ~moj_string(); char *Povedz_obsah(); int Povedz_dlzku(); void Zmen_obsah(int d, char *retazec); };
37
Kopírovací konštruktor
Definujeme kopírovací konštruktor: moj_string::moj_string(moj_string &str) { dlzka=str.Povedz_dlzku(); s=new char[dlzka]; for (int i=0;i<dlzka;i++) s[i]=str.s[i]; } Hlavný program: moj_string s1(36,"Toto je test novej triedy moj_string"); moj_string s2=s1; //Tu sa zavolá kopírovací konštruktor cout.write(s1.Povedz_obsah(),s1.Povedz_dlzku()); cout<<endl; cout.write(s2.Povedz_obsah(),s2.Povedz_dlzku()); s1.Zmen_obsah(19,"Podarilo sa nam to?");
38
Kopírovací konštruktor
Kým bez kopírovacieho konštruktora bol výstup takýto: s kopírovacím konštruktorom dostaneme správny výstup:
39
Kopírovací konštruktor
Pomocou kopírovacieho konštruktora môžeme poslať objekt do funkcie ako parameter. Aj keď vytváranie kópie je samo o sebe nevýhodné vzhľadom na pamäť, dá sa urobiť technicky korektne. Príklad: Počítanie obvodu n-uholníka Už máme vytvorenú triedu nUholník, obohatíme ju o kopírovací konštruktor: class nUholnik { private: Bod *B; int n; public: nUholnik(int n, Bod *B); nUholnik(nUholnik &U); ~nUholnik(); void Vypis_suradnice(string meno); void Zmen_suradnice(Bod *B) {for (int i=0;i<n;i++) this->B[i]=B[i];} int N() {return n;} int BX(int index) {return B[index-1].X();} int BY(int index) {return B[index-1].Y();} };
40
Kopírovací konštruktor
Kopírovací konštruktor bude definovaný takto: nUholnik::nUholnik(nUholnik &U) { cout<<"kopirovaci konstruktor"<<endl; n=U.n; B=new Bod[n]; for (int i=0;i<n;i++) B[i]=U.B[i]; } V triede Kalkulačka bude len jediná funkcia Obvod, ktorej paramterom bude objekt triedy nUholník: class Kalkulacka { public: float Obvod(nUholnik U); };
41
Kopírovací konštruktor
Hlavný program: int main(int argc, char* argv[]) { Bod B[4]; B[0].Zmen_suradnice(0,0); B[1].Zmen_suradnice(1,0); B[2].Zmen_suradnice(1,1); B[3].Zmen_suradnice(0,1); nUholnik Stvorec(4,B); Kalkulacka K; Stvorec.Vypis_suradnice("Stvorec"); cout<<"Obvod stvorca je: "<<K.Obvod(Stvorec)<<endl; return 0; }
42
Kopírovací konštruktor
Keď sme nemali k dispozícii kopírovací konštruktor, program skrachoval: Pomocou kopírovacieho konštruktora môžeme pádu programu predísť:
43
Kopírovací konštruktor
Nakoniec si ukážeme aj to, ako sa dá kopírovací konštruktor využiť, ak je objekt návratovou hodnotou funkcie: Príklad: Posúvanie n-uholníka Do triedy Kalkulačka doplníme funkciu, ktorá bude posúvať daný n-uholník o danú dĺžku v smere x a y. Návratovou hodnotou tejto funkcie bude posunutý n-uholník. class Kalkulacka { public: float Obvod(nUholnik U); nUholnik Posun(nUholnik &U, int dx, int dy); }; nUholnik Kalkulacka::Posun(nUholnik &U, int dx, int dy) Bod *B=new Bod[U.N()]; for (int i=0;i<U.N();i++) B[i].Zmen_suradnice(U.BX(i+1)+dx,U.BY(i+1)+dy); nUholnik U1(U.N(),B); delete[] B; return U1; }
44
Kopírovací konštruktor
Hlavný program: int main(int argc, char* argv[]) { Bod B[4]; B[0].Zmen_suradnice(0,0); B[1].Zmen_suradnice(1,0); B[2].Zmen_suradnice(1,1); B[3].Zmen_suradnice(0,1); nUholnik Stvorec(4,B); Kalkulacka K; Stvorec.Vypis_suradnice("Stvorec"); cout<<"Obvod stvorca je: "<<K.Obvod(Stvorec)<<endl; nUholnik PosunutyStvorec=K.Posun(Stvorec,1,1); PosunutyStvorec.Vypis_suradnice("Posunuty stvorec"); return 0; }
45
Kopírovací konštruktor
Opäť platí, že bez kopírovacieho konštruktora by program zlyhal:
46
Kopírovací konštruktor
S kopírovacím konštruktorom máme problém vyriešený:
47
Zhrnutie Preťažovanie je viacúčelové využitie funkcií alebo operátorov
Každá funkcia môže mať ľubovoľne veľa variantov, s rôznym počtom aj typom parametrov a s rôznymi návratovými hodnotami Preťaženie sa dá dosiahnuť aj použitím štandardných argumentov, pretože tú istú funkciu potom môžeme volať s rôznym počtom parametrov Pri nesprávnom návrhu variantov funkcie môže preťažovanie viesť k nejednoznačnostiam, ktoré môžu viesť až k chybám kompilácie Tak ako každú funkciu, môžeme preťažiť aj konštruktor a inicializovať tak objekty rôznymi spôsobmi Kopírovací konštruktor je špeciálny typ konštruktora, ktorý zabraňuje nesprávnemu zaobchádzaniu s pamäťou. Spúšťa sa automaticky, ak sa objekt inicializuje priradením, ak je objekt parametrom alebo návratovou hodnotou funkcie
48
Preťažovanie operátorov
Tak ako môžeme preťažovať funkcie, môžeme preťažiť aj operátory (+,-,=,++,[]...), ktoré sú vlastne istým typom funkcií. Takto môžeme napr. definovať operáciu + pre zlomky, komplexné čísla atď. Syntax preťaženia operátora: deklarácia: typ operator#(parametre); definícia: typ meno_triedy::operator#(parametre) { ... } Pri preťažovaní operátorov platia základné obmedzenia: 1. Nemôžeme zmeniť počet operandov operátora 2. Nedá sa zmeniť priorita operátora
49
Preťažovanie binárnych operátorov
Sem patria operátory +, -, *, /, = Príklad: Počítanie s komplexnými číslami V triede KomplexnéČíslo vytvoríme operátory + a * tak, aby sme mohli sčítať a vynásobiť dve komplexné čísla a tiež vynásobiť komplexné číslo reálnym. class KomplexneCislo { private: float a, b; public: KomplexneCislo() {} KomplexneCislo(float a, float b) {this->a=a; this->b=b;} float A() {return a;} float B() {return b;} void ZmenA(float a) {this->a=a;} void ZmenB(float b) {this->b=b;} void Vypis() {if (b>=0) cout<<a<<"+"<<b<<"i"; else cout<<a<<b<<"i";} KomplexneCislo operator+(KomplexneCislo x); KomplexneCislo operator*(KomplexneCislo x); KomplexneCislo operator*(float t); };
50
Preťažovanie binárnych operátorov
Definujeme jednotlivé operátory: KomplexneCislo KomplexneCislo::operator +(KomplexneCislo x){ KomplexneCislo z; z.ZmenA(a+x.a); z.ZmenB(b+x.b); return z; } KomplexneCislo KomplexneCislo::operator *(KomplexneCislo x){ z.ZmenA(a*x.a-b*x.b); z.ZmenB(a*x.b+b*x.a); KomplexneCislo KomplexneCislo::operator *(float t){ z.ZmenA(a*t); z.ZmenB(b*t);
51
Preťažovanie binárnych operátorov
KomplexneCislo x(1,2), y(3,-1), z1, z2, z3; z1=x+y; z2=x*y; z3=x*3; cout<<"Komplexne cisla: "; x.Vypis(); cout<<", "; y.Vypis(); cout<<endl<<"x+y: "; z1.Vypis(); cout<<endl<<"x*y: "; z2.Vypis(); cout<<endl<<"x*3: "; z3.Vypis(); Výstup: Komplexne cisla: 1+2i, 3-1i x+y: 4+1i x*y: 5+5i x+3: 3+6i
52
Preťažovanie relačných operátorov
Ide o operátory ==, <, >, <=, >= ... Príklad: Porovnávanie veku dvoch osôb Budeme pracovať s triedou Dátum, ktorú sme už vytvorili. Definujeme pre ňu operátory <, >, ==. class Datum { private: int den, mesiac, rok; public: Datum(char *s) {sscanf(s,"%i%*c%i%*c%i",&den,&mesiac,&rok);} Datum(int d, int m, int r) {den=d; mesiac=m; rok=r;} void Vypis() {cout<<den<<". "<<mesiac<<". "<<rok<<endl;} int Den() {return den;} int Mesiac() {return mesiac;} int Rok() {return rok;} bool operator >(Datum dat); bool operator <(Datum dat); bool operator ==(Datum dat); };
53
Preťažovanie relačných operátorov
Definujeme jednotlivé operátory: bool Datum::operator <(Datum dat) { if (rok<dat.rok) return true; else if (rok>dat.rok) return false; else if (mesiac<dat.mesiac) return true; else if (mesiac>dat.mesiac) return false; if (den<dat.den) return true; else return false; }
54
Preťažovanie relačných operátorov
bool Datum::operator >(Datum dat) { if (rok>dat.rok) return true; else if (rok<dat.rok) return false; else { if (mesiac>dat.mesiac) return true; else if (mesiac<dat.mesiac) return false; if (den>dat.den) return true; else return false; } bool Datum::operator ==(Datum dat) { if (rok==dat.rok && mesiac==dat.mesiac && den==dat.den) return true;
55
Preťažovanie relačných operátorov
char meno1[30], meno2[30], sdat1[10], sdat2[10]; cout<<"Meno a datum narodenia prvej osoby: "<<endl; cin.getline(meno1,31); cin.getline(sdat1,11); cout<<"Meno a datum narodenia druhej osoby: "<<endl; cin.getline(meno2,31); cin.getline(sdat2,11); Datum dat1(sdat1), dat2(sdat2); if (dat1<dat2) cout<<meno1<<" je starsi ako "<<meno2<<endl; else if (dat1>dat2) cout<<meno2<<" je starsi ako "<<meno1<<endl; else if (dat1==dat2) cout<<"Tito dvaja su rovnako stari"<<endl; Meno a datum narodenia prvej osoby: Jana Kovacova Meno a datum narodenia druhej osoby: Jan Kovac Jan Kovac je starsi ako Jana Kovacova
56
Preťažovanie unárnych operátorov
Sem patria napr. operátory ++, -- Príklad: Určenie nasledujúceho dňa Pre triedu Dátum definujeme operátor ++ (nasledujúci deň) class Datum { private: int den, mesiac, rok; public: Datum(char *s) {sscanf(s,"%i%*c%i%*c%i",&den,&mesiac,&rok);} Datum(int d, int m, int r) {den=d; mesiac=m; rok=r;} void Vypis() {cout<<den<<". "<<mesiac<<". "<<rok<<endl;} int Den() {return den;} int Mesiac() {return mesiac;} int Rok() {return rok;} bool operator >(Datum dat); bool operator <(Datum dat); bool operator ==(Datum dat); Datum operator ++(); };
57
Preťažovanie unárnych operátorov
Definujeme operátor ++: Datum Datum::operator ++() { if (den==31 && mesiac==12) rok++; mesiac=1; den=1; } else if (den==PocetDni[mesiac-1]) mesiac++; else den++; return *this;
58
Preťažovanie unárnych operátorov
Hlavný program: char sdat[10]; cout<<"Aky je dnesny datum? "; cin.getline(sdat,11); Datum dat(sdat); dat++; cout<<"Zajtrajsi datum je: "; dat.Vypis(); Výstup:
59
Preťažovanie operátora priradenia
Už sme si hovorili, aké problémy môžu nastať pri priraďovaní objektov, ak súčasťou objektu je dynamicky alokovaná pamäť. Teraz si ukážeme, ako môžeme priraďovací operátor vhodne preťažiť, aby sme sa problémom vyhli. Príklad: Priraďovanie objektov triedy moj_string class moj_string { private: char *s; int dlzka; public: moj_string(int d, char *retazec); moj_string(moj_string &str); ~moj_string(); char *Povedz_obsah(); int Povedz_dlzku(); void Zmen_obsah(int d, char *retazec); moj_string &operator =(moj_string &str); };
60
Preťažovanie operátora priradenia
Teraz definujeme operátor priradenia. Tento operátor má ako parameter odkaz na objekt triedy moj_string a návratová hodnota je takisto odkaz. moj_string &moj_string::operator =(moj_string &str) { if (dlzka<str.dlzka) //ak je nový retazec dlhší, realokujeme pamäť delete[] s; s=new char[str.dlzka]; } dlzka=str.dlzka; for (int i=0;i<dlzka;i++) s[i]=str.s[i]; return *this;
61
Preťažovanie operátora priradenia
Aby sme mohli monitorovať manipulovanie programu s pamäťou, v konštruktore a deštruktore sa bude vypisovať informácia o adrese, kde sa pamäť alokuje alebo dealokuje. moj_string::moj_string(int d, char *retazec):dlzka(d) { s=new char[dlzka]; cout<<"Alokujem pamat na adrese: "<<(void*)s<<endl; for (int i=0;i<dlzka;i++) s[i]=retazec[i]; } moj_string::~moj_string() cout<<"Dealokujem pamat na adrese: "<<(void*)s<<endl; delete[] s;
62
Preťažovanie operátora priradenia
Hlavný program: int main() { moj_string s1(36,"Toto je test novej triedy moj_string"); moj_string s2(24,"Napiseme zakerny program"); cout.write(s1.Povedz_obsah(),s1.Povedz_dlzku()); cout<<endl; cout.write(s2.Povedz_obsah(),s2.Povedz_dlzku()); s2=s1; return 0; }
63
Preťažovanie operátora priradenia
Program s klasickým operátorom priradenia skrachoval: Naopak program s preťaženým operátorom funguje správne:
64
Preťažovanie indexového operátora
Príklad: Indexový operátor [ ] pre triedu nUholník Do triedy nUholník doplníme indexový operátor ktorý bude vracať daný vrchol n-uholníka (objekt typu Bod) class nUholnik { private: Bod *B; int n; public: nUholnik(int n, Bod *B); nUholnik(nUholnik &U); ~nUholnik(); void Vypis_suradnice(string meno); void Zmen_suradnice(Bod *B) {for (int i=0;i<n;i++) this->B[i]=B[i];} int N() {return n;} //int BX(int index) {return B[index-1].X();} //int BY(int index) {return B[index-1].Y();} Bod operator [](int index) {return B[index-1];} };
65
Preťažovanie indexového operátora
Pomocou nového operátora potom môžeme prepísať napr. funkciu Obvod triedy Kalkulačka: float Kalkulacka::Obvod(nUholnik U) { float o=0.; for (int i=1;i<U.N();i++) o+=sqrt(pow(U.BX(i+1)-U.BX(i),2)+pow(U.BY(i+1)-U.BY(i),2)); o+=sqrt(pow(U.BX(U.N())-U.BX(1),2)+pow(U.BY(U.N())-U.BY(1),2)); return o; } o+=sqrt(pow(U[i+1].X()-U[i].X(),2)+pow(U[i+1].Y()-U[i].Y(),2)); o+=sqrt(pow(U[U.N()].X()-U[1].X(),2)+pow(U[U.N()].Y()-U[1].Y(),2));
66
Zhrnutie Takmer všetky operátory sa dajú preťažiť
Pri preťažovaní operátorov musíme dodržiavať dve základné pravidlá: 1. Nesmieme meniť počet operandov operátora 2. Nedá sa meniť priorita operátora
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.