Zklady objektovo orientovanho programovania pokraovanie al vznam opertora
Základy objektovo orientovaného programovania pokračovanie
Ďalší význam operátora rozsahu platnosti : : Používa sa na definíciu funkcií mimo triedy class C{ public: int fnc(int ); …. }; int C: : fnc(int a){ … return a; } Stavba programu Hlavičkové súbory name. h - obsahujú deklarácie tried Súbory name. cpp obsahujú definície funkcií tried a pritom deklarácie tried musia byť do súborov inkludované.
Pr. name. h class_name 1{ … }; class_name 2{ … }; name. cpp #include <name 1. h> // obsahuje deklaráciu knižničných tried #include “name. h” // obsahuje deklarácie vlastných tried. . . //Nasledujú definície funkcií vlastných tried returntype class_name: : fnc 1( ){ … } … returntype main( ) { … }
Inline funkcie class C{ public: float f(float a, float b){ // inline by default float c = a*b; return c; } inline void g( ); // inline function given explicitly }; void C: : g( ){ … } 1. Funkcia definovaná v triede je implicitne inline. 2. Funkcia deklarovaná ako inline je explicitne inline aj keď jej telo je definované mimo triedu. Každé volanie inline funkcie je nahradené jej telom. Náhradu zabezpečí kompilátor.
Kopírovací konštruktor Jeho existenciu predpokladá kompilátor automaticky. class_name: : class_name(class_name& c){ data_member = c. data_member; } // vytvorí kópiu objektu Je automaticky volaný keď je parametrom funkcie objekt daný hodnotou resp. keď je návratovou hodnotou funkcie objekt daný hodnotou class C{ … }; void f(C c){…} //object pass by value C h( ){ //object return by value C c 1; return c 1; } Je to neefektívny spôsob deklarácie funkcie: - zbytočne sa zaberá pamäťové miesto veľkosti sizeof(C) - zbytočne je volaný kopírovací konštruktor
Objekt daný referenciou je výhodnejší spôsob poskytnutia resp. deklarácie návratovej hodnoty funkcie. void g(C& c){…} //pass by reference C& k(C& c 1){ //return by reference … return c 1; } Aby sa zabránilo neefektívemu volaniu funkcií mimo triedy s objektom daným hodnotou resp. jeho vráteniu z funkcie hodnotou môžno deklarovať kopírovací konštruktor v private časti triedy: class C{ private: C(C& ); // copy constructor … public: } Kompilátor potom nedovolí neefektívny spôsob deklarácie funkcie.
Príklad: class C{ …. } C& f(C& c){ return c; } C& g( ){ C c; return c; } // **** DANGER! c goes out of existence C h( ){ C c; return c; } // return by value int main{ C c 1, c 2; c 2 = f(c 1); // OK c 2 = g( ); // **** Caution: nonexistence reference c 2 = h( ); // OK return 0; }
Členy tried typu static Všetky objekty triedy zdieľajú premennú deklarovanú ako static. Definícia premennej sa uvádza mimo triedy. #include <stdio. h> class counter{ static int value; public: void increment(){ value++; } void decrement(){ value--; } int access_value() {return value; } }; int counter: : value = 0; main(){ counter c 1, c 2; for (int i =1; i <=5 i++) c 1. increment(); // printf(“n c 1=%d”, c 1. access_value()); //c 1=5 printf(“n c 2=%d”, c 2. access_value()); //c 2=5 }
Premenná aj funkcia typu static sa môže sprístupňovať aj bez udania objektu preto ich nazývame aj členmi triedy. class C{ public: static int a; static void f(); }; main(){ C c 1; int x = c 1. a; int z = C: : a; c 1. f(); C: : f(); } //access through an object //access directly //through object //directly
Statické funkcie majú prístup len k statickým členom triedy. Statické funkcie nesmú byť definované inline. class C{ static int a; int *b; public: static int geta(int arg ); //{ ERROR defined inline … // } }; C: : geta( int arg){ b=arg; // ERROR b is non static return (a=arg); // OK } Lokálne premenné vo funkciách môžu byť statické v takom prípade všetky objekty keď volajú funkciu zdieľajú túto statickú premennú.
#include <iostream. h> class C{ public: void m(); private: int x; }; void C: : m(){ static int s=0; cout << ++s << endl; } int main(){ C c 1, c 2; c 1. m(); //s je 1 c 2. m(); // s je 2 c 1. m(); // s je 3 return 0; }
Rezervované slovo this Premenná typu smerník this obsahuje adresu aktuálneho objektu. Aktuálny objekt je objekt, ktorému bola poslaná správa. Použitie: -Pri riešení konfliktov ak napr. premenná deklarovaná v triede sa nazýva rovnako lokálna premenná vo funkcii. class counter{ private: int value; static int max. Value; public: counter(){ value =0; } void increment(); void decrement(); void put_value(int value){this->value = value; } int access_value(); };
-Na kontrolu či argument členskej funkcie objektu je samotný objekt. #include <iostream. h> class C{ public: int isitme(C& ); }; int C: : isitme(C& arg){ if (&arg = = this) return 1; else return 0; } int main(){ C a; C* b = &a; if (b->isitme(a)) cout << “yes, &a is b” <<endl; return 0; }
Nech trieda File obsahuje funkciu copy(File& dest), ktorá kopíruje objekt, ktorý funkciu volá do objektu daného argumentom funkcie. Aby sa zamedzilo nežiadúcemu efektu, že objekt, ktorý volá funkciu sa prepíše sám f 1. copy(f 1) musí byť funkcia definovaná nasledovne: void File: : copy(File& dest) { if(this == &dest) // can’t copy file to itself return; else … // copy this file to dest }
this je implicitne const this je nie dostupné v statických funkciách Pr. class C{ public: void m(const C& obj){ this = &obj; // ERROR: this is a constant } static void s( ){ this->count = 0; // ERROR: static method } private: static int count; };
Triedy definované rezervovaným slovom struct Jazyk C++ rozširuje význam struct na význam class s tým rozdielom, že členy štruktúry sú náhradne public na rozdiel od triedy, kde sú private. Nasledujúce dve deklarácie sú ekvivalentné: class C{ int x; // private by default public: void f(); }; struct C{ void f(); private: int x; }; // public by default
friend funkcie a friend triedy Ak v deklarácii označí trieda nejakú funkciu friend, umožní tejto funkcii prístup do svojej private časti. Ak označí tak triedu, všetky funkcie z tejto triedy získajú prístup do jej private časti. Pr. class A; class B{ public: void incr 2(A& object); void incr 3(A& object); }; class C{ public: void incr 4(A& object); void incr 5(A& object); };
class A{ private: int value; public: A(){value = 0; } friend void incr 1(A& object); friend void B: : incr 2(A& object); friend C; }; void incr 1(A& object){ object. value++; } void B: : incr 2(A& object){object. value++; } void B: : incr 3(A& object){object. value++; } //nedovolené void C: : incr 4(A& object){object. value++; } void C: : incr 5(A& object){object. value++; }
Prekrývanie štandardných operátorov C++ umožňuje použitie základných operátorov jazyka pre triedy ako pre základné typy. Zoznam všetkých operátorov, ktoré môžu byť prekryté: + - * / = < > += -= *= /= << >> <<= >>= == != <= >= ++ -- % & ^ ! | ~ &= ^= |= && || %= [] () -> new delete Operátory, ktoré nemožno prekryť: . operátor prístupu k členom triedy. * člen triedy dereferenčný operátor : : operátor rozsahu ? : podmienený operátor sizeof operátor
Na prekrytie operátora potrebujeme napísať funkciu operator za ktorým nasleduje znamienko operátora, ktorý chceme prekryť Type operator sign (paramerers) V nasledujúcom príklade definujeme funkciu, ktorá prekryje operátor sčítania + na sčítanie dvojrozmerných vektorov: class CVector{ private: int x, y; public: CVector(){x=0; y=0; } Cvector(int, int ); Cvector operator + (Cvector&); };
CVector : : CVector (int a, int b){ x = a; y = b; } CVector : : operator + (Cvector& param){ CVector temp; temp. x = x + param. x; temp. y = y + param. y; return temp; } int main(){ CVector a (3, 1); CVector b (1, 2); CVector c; c = a + b; // 1 spôsob volania c = a. operator+ (b); // 2 spôsob volania return 0; }
Prekrytie operátora pomocou globálnej funkcie (okrem operátorov [ ], =, ( ), -> , ktoré môžu byť prekryté len funkciami triedy). Príklad: Prekrytie operátora + globálnou funkciou pre sčítanie dvoch objektov triedy C class C{ … }; C operator + (C& c 1, C& c 2){ … } C a, b, c; // objekty triedy C volanie: a = operator+(b, c); alebo a= b+c;
#include <iostream. h> class CVector { private: int x, y; public: CVector ( ) { }; CVector (int, int); friend CVector operator + (CVector&, CVector&); }; CVector: : CVector (int a, int b) { x = a; y = b; } CVector operator+ (CVector& param 1, CVector& param 2) { CVector temp; temp. x = param 1. x + param 2. x; temp. y = param 1. y + param 2. y; return (temp); }
int main ( ) { CVector a (3, 1); CVector b (1, 2); CVector c; c = a + b; c = operator + (a, b); cout << c. x << ", " << c. y; return 0; } // iný spôsob volania Operátor posuvu vpravo << je v systémových knižniciach prekrytý členskou funkciou pre formátovaný výstup štandardných typov cout<< i alebo cout. operator<<(i) Podobne pre formatovaný vstup je prekrytý operator >> cin>>i alebo cin. operator>>(i) Pre užívateľsky definovaný typ (trieda) operátory << a >> musia byť prekryté globálnou funkciou, ktorú napíše užívateľ.
Expression @a a@ a@b a(b, c. . . ) a->b Operator (@) Function member + - * & ! ~ ++ -A: : operator@( ) ++ -A: : operator@(int) +-*/%^&| < > == != <= >= << >> && || , A: : operator@(B) = += -= *= /= % = ^= &= |= <<= >>= [ ] A: : operator@(B) () A: : operator( )(B, C. . . ) -> A: : operator->( ) Kde a, b, c sú objekty tried A, B, C. znak @ sa nahradí príslušným operátorom Global function operator@(A) operator@(A, int) operator@(A, B) -
Zhrnutie: Ak prekryjeme binárny operátor členskou funkciou triedy, tak funkcia má iba jeden parameter. Ak prekryjeme binárny operátor globálnou funkcia má dva parametre. Podobne pre unárne operátory s tým rozdielom, že prekrytie členskou funkciou triedy je bez parametra a prekrytie globálnou funkciou má jeden parameter. (Výnimkou sú operátory ++ a --)
Prekrytie niektorých špeciálnych operátorov Prekrytie operátora indexovania [ ] class C{ … returntype operator[ ](paramtype); … }; Volanie: c[i] alebo c. operator[ ](i) Pr. Kontrola indexovania hraníc poľa const int Max. Size = 100; class int. Array{ private: int a[Max. Size]; int size; public: int. Array(int s); int& operator[ ](int); int get_size( ){return size; } };
int& int. Array: : operator[ ](int i){ if(i< 0 || i >= size){ cerr << “index “<< i << “out of bounds”; return a[0]; } return a[i]; } int. Array: : int. Array( int s){ if( s <= Max. Size) size = s; else{ cerr << “Array to big”n; exit(0); } } int main{ int. Array b(5); for (int j=0; j<b. get_size( ); j++) b[j] = 2*j; for (int j=0; j<6; j++) cout << b[j] << endl; return 0; }
Prekrytie operátora volania funkcie ( ) class C{ … returntype operator ( ) (paramtype) … }; Volanie: c(x, y) alebo c. operator( )(x, y) Pr. Indexovanie 2 -rozm pola Miesto arr [i][j] možno písať arr (i, j) Prekrytie operátorov incrementovania ++ a dekrementovania -x++ operator++( ) ++x operator++(int a) parameter int a sa nepoužíva slúži len na rozlíšenie post od pre inkrementovania. Podobne pre --
// Generator pseudonahodnych cisel v rozsahu 0. . limit-1 // Pretazeny operator ( ) vracia pseudonahodne cislo #include <iostream. h> #include <limits. h> #include <stdlib. h> class generator { int limit; public: void Nastav. Mez(long mez) {limit = mez; } generator(int mez = INT_MAX): limit(mez) {} virtual int operator( )( ); }; int generator: : operator ( )(void) { return rand( ) % limit; // Pouzijeme standardni generator } generator G 6(6); // Instancia: generator nahodnych celych cisel v rozsahu 0. . 5 void main( ){ for(int i = 0; i < 10; i++) cout << G 6( ) << endl; }
// Prefixovy a postfixovy operator ++ pre vypoctovy typ #include <iostream. h> enum dni {po, ut, st, ct, pa, so, ne}; dni operator++(dni& d) { int i = d; i++; if(i > ne) i = po; d = dni(i); return d; } dni operator++(dni& d, int) { dni D = d; int i = d; i++; if(i > ne) i = po; d = dni(i); return D; } // Prefixovy operator // Tu sa pouzije operator ++ pro cela cisla // Zmenime operand // a zmeneny operand vratime // Postfixovy operator // Zapamatame si povodnu hodnotu // zmenime operand // a povodnu hodnotu vratime
void main( ) { dni d = ut; dni e = ++d; cout<<e<<endl; e = d++; cout<<e<<endl; }
// Priklad pretazeneho operatora -> #include <iostream. h> struct datum { int den, mesic, rok; datum(int d, int m, int r): den(d), mesic(m), rok(r){} }; class p_datum { datum *p; public: p_datum(datum* pd): p(pd){} ~p_datum( ){ delete p; } datum* operator->( ) { return p; } }; void main( ) { datum *dat = new datum(11, 12, 2000); p_datum d (dat); cout << d->den <<". "<< d->mesic <<". " << d->rok << endl; }
//Priklad preddefinovania operatora new #include <stdlib. h> #include <string. h> #include <stdio. h> void * operator new(size_t s) { if(!s) s = 1; void *p = malloc(s); if(p) memset(p, 0 x. FF, s); return p; } // Vrat vzdy aspon 1 bajt // alokuj pamet // vypln ju jednickami // Vrat jej adresu void operator delete(void* p) { printf("uvolnujem objekt na adrese %pn", p); if(p) free(p); } size_t s 1 = 100; size_t s 2 = 200;
void main( ) { int *ui = new int(s 1); double* db = new double(s 2); delete ui; delete db; }
Namespaces Umožňujú zoskupovať množinu tried objektov a funkcií pod jedno meno. namespace identifier { namespace-body } Použitie je vhodné v prípade, že globálny objekt alebo funkcia môžu mať to isté meno ako iná. #include <iostream. h> namespace first { int var = 5; } namespace second { double var = 3. 14; } int main(){ cout << first: : var << endl; cout << second: : var << endl; return 0; }
using namespace Slúži na pričlenenie daného úseku programu k namespace s daným identifikátorom tak, aby objekty a funkcie z namespace boli dostupné priamo ako keby boli definované v globálnom rozsahu. Použitie: using namespace identifier; #include <iostream. h> namespace first{ int var = 5; } namespace second{ double var = 3. 14; } int main(){ { using namespace first; cout << var << endl; } { using namespace second; cout << var << endl; } retern 0; }
// Vnoreny priestor mien // Program obsahuje priestor mien Vonkajsi a do neho vnoreny priestor mien Vnoreny // Ak pouzijeme identifikator z vnutorneho priestoru mien // - vo vnutornom priestore ho nemusime kvalifikovat // - ve vonkajsom priestore, ho staci kvalifikovat menom vnutorneho priestoru, // - kdekolvek inde, ho musime kvalifikovat menom vnutorneho aj vonkajsieho priesto #include <iostream> #include<string> using namespace std; namespace Vonkajsi { string Text = "Vonkajsi"; void Tlac( ){cout << Text << endl; } namespace Vnoreny { // Vnoreny priestor mien string Text = "Vonkajsi: : Vnoreny"; void Tlac( ){cout << Text << endl; } } // Koniec vnoreneho priestor void Tlac_znova( ) { cout << Vnoreny: : Text << endl; } }
int main( ){ Vonkajsi: : Tlac( ); Vonkajsi: : Vnoreny: : Tlac( ); Vonkajsi: : Tlac_znova( ); return 0; } Výstup: Vonkajsi: : Vnoreny
Cvičenie 1. Určite, ktoré konštruktory sú v hlavnom programe volané a kde: void f(C); // prototypy funkcií C g( ); void h(C&); C& k(C&); void main( ){ C c 1, c 2; f(c 1); c 2= g( ); h( c 2); c 2= k(c 1); }; 2. Určite a vysvetlite chyby v programe : class C{ public: C(int a): y(a){ }; void f( ){++x; } static void g( ){y++; } private: static int x; int y; };
void main( ){ C c 1(0); int C: : x=0; c 1. f( ); C: : g( ); }; 3. Určite chybu v programe class C{ int x; public: static int f( int a){x= a; } … }; 4. Napíšte prekrytie operátora ( ) pre indexovanie 2 -rozmerného poľa celých čísiel b(i, j) namiesto b[i][j] s kontrolou hraníc poľa. Návod: Definujte konštruktor pre inicializáciu hraníc poľa. Definujte operátor(int i, int j), ktorý kontroluje či parametre sú z prípustnej oblasti ak ano vracia a[i*size 2+j]
5. Prekryte operátor ~ friend funkciou tak, aby vracal komplexne združené číslo k objektu triedy Complex. 6. Prekryte operátor ! friend funkciou tak, aby vracal absolútnu hodnotu objektu triedy Complex. Návod: Deklarujte triedu Complex (komplexných čísiel)
- Slides: 42