440 likes | 833 Views
Objektno orijentisano programiranje. Preklapanje operatora. Preklapanje operatora. Pojam preklapanja operatora Operatorske funkcije Funkcije članice i globalne funkcije Bočni efekti i veze između operatora Unarni i binarni operatori Izbor povratnih vrijednosti operatorskih funkcija.
E N D
Objektnoorijentisano programiranje Preklapanje operatora
Preklapanje operatora • Pojam preklapanja operatora • Operatorske funkcije • Funkcije članice i globalne funkcije • Bočni efekti i veze između operatora • Unarni i binarni operatori • Izbor povratnih vrijednosti operatorskih funkcija
Pojam preklapanja operatora • Preklapanje operatora (operator overloading) • predstavlja redefinisanje postojećih operatora zbog potrebe da na specifičan način rade sa novim tipovima podataka (npr. klasnim tipovima) • omogućava da se ugrađeni C++ operatori prilagode tako da izvršavaju određenu akciju nad objektima klasnih tipova
Pojam preklapanja operatora (nastavak) • Moguće je izvršiti preklapanje skoro svih operatora u jeziku C++ • Zavisno od konteksta u kome je upotrijebljen operator generiše se odgovarajući kod pri prevođenju programa • Koncept preklapanja operatora treba pažljivo koristiti, zbog mogućih neželjenih efekata • Primjeri korištenja preklapanja operatora • operator dodjele se koristi na isti način za različite tipove podataka • operator sabiranja se koristi i za cijele i za realne brojeve
Pojam preklapanja operatora (nastavak) • Za korištenje operatora nad objektima klasnih tipova potrebno je preklapanje operatora, osim u dva slučaja: • operator dodjele = se može koristiti i bez redefinisanja, a može se i eksplicitno redefinisati • adresni operator & se može koristiti i bez redefinisanja, a može se i eksplicitno redefinisati • Preklapanje se vrši tako što se definiše operatorska funkcija • Operatorske funkcije se implementiraju kao metode ili kao globalne funkcije (obično prijateljske funkcije klasa)
Pojam preklapanja operatora (primjer) • Pretpostavimo da su u programu potrebni kompleksni brojevi i operacije nad njima. • Tip kompleksnog broja će realizovati klasa koja sadrži elemente (real, imag), a takođe i funkcije za operacije. • Pogodno je da se pozivi funkcija koje realizuju operacije mogu notacijski predstaviti standardnim operatorima. • U jeziku C++, operatori za korisničke tipove (klase) su specijalne operatorske funkcije. • Operatorske funkcije nose ime operator@, gde je @ neki operator ugrađen u jezik. • Operatorske funkcije preklapaju standaradne operatore (+, -, *, /, ...).
Operatorske funkcije • Operatorske funkcije se mogu koristiti u izrazima kao i operatori nad ugrađenim tipovima. • Ako je operatorska funkcija definisana na prethodno opisan način, izraz t1@t2 se tumači kao operator@(t1,t2):
Osnovna pravila preklapanja operatora • C++ dozvoljava preklapanje operatora kao što dozvoljava i preklapanje imena funkcija. • Princip preklapanja omogućava da se definišu nova značenja operatora za korisničke tipove.
Osnovna pravila preklapanja operatora (nastavak) • Postoje neka ograničenja u preklapanju operatora: • ne mogu da se preklope operatori ., .*, ::, ?: i sizeof, dok svi ostali mogu; • ne mogu da se redefinišu značenja operatora za primitivne (standardne) tipove podataka; • ne mogu da se uvode novi simboli za operatore; • ne mogu da se mijenjaju osobine operatora koje su ugrađene u jezik: n-arnost, prioritet i asocijativnost.
Operatorske funkcije – pravila • Operatorske funkcije mogu biti: • funkcije članice klase kod kojih je skriveni argument lijevi operand ili • globalne funkcije (uglavnom prijatelji klasa) kod kojih je bar jedan argument tipa korisničke klase. • Za korisničke tipove su unaprijed definisana tri operatora: = (dodela vrednosti), & (uzimanje adrese) i , (lančanje). • Sve dok ih korisnik ne redefiniše, oni imaju podrazumijevano značenje.
Operatorske funkcije – pravila (nastavak) • Podrazumijevano značenje operatora = je kopiranje objekta član po član. • Pri kopiranju članova tipa klase, pozivaju se operatori = klasa kojima članovi pripadaju. • Ako je član objekta pokazivač, kopiraće se samo taj pokazivač, a ne i pokazivana vrijednost. • Kada treba kopirati i objekat na koji ukazuje član-pokazivač korisnik tada treba da redefiniše operator =. • Vrijednosti operatorskih funkcija mogu da budu bilo kog tipa, pa i void. • Ako se simbol operatora sastoji od slova (npr. new), mora se pisati odvojeno od ključne riječi operator.
Bočni efekti i veze između operatora • Bočni efekti koji postoje kod operatora za ugrađene tipove nikad se ne podrazumijevaju za redefinisane operatore. • To važi za operatore ++ i -- (prefiksne i postfiksne) i sve operatore dodjele (=, +=, -=, *= itd.). • Operator = (i ostali operatori dodjele) ne mora da mijenja stanje objekta; ipak, ovakve upotrebe treba izbjegavati. • Strogo se preporučuje da operatori koje definiše korisnik imaju očekivano značenje, radi čitljivosti programa. • Npr. ako su definisani i operator += i operator +, dobro je da a+=b ima isti efekat kao i a=a+b.
Bočni efekti i veze između operatora (nastavak) • Veze koje postoje između operatora za ugrađene tipove se ne podrazumijevaju za redefinisane operatore. • Npr, ako je definisan operator +, a+=b ne znači automatski a=a+b, (+= mora posebno da se definiše). • Kada se definišu operatori za klasu, treba težiti da njihov skup bude kompletan. • Npr, ako su definisani operatori = i +, treba definisati i operator +=; ili, za == treba definisati i !=.
Operatorske funkcije kao članice ili kao globalne funkcije • Ako je @ neki binarni operator (npr +), on može da se realizuje: • Kao funkcija članica klase X (mogu se argumenti prenositi i po referenci) • Kao prijateljska globalna funkcija • Nije dozvoljeno da se u programu nalaze obje ove funkcije.
Operatorske funkcije kao članice <tip> operator@ (X) Poziv a@b se sada tumači kao: a.operator@(b) , za funkciju članicu
Operatorske funkcije kao prijateljske funkcije <tip> operator@ (X,X) Poziv a@b se sada tumači kao: operator@(a,b) , zaglobalnu funkciju
Operatorske funkcije kao funkcije članice ili prijateljske funkcije • Kod operatorske funkcije članice klase lijevi operand je skriveni argument – objekat date klase. • Ako lijevi operand treba da bude standardnog tipa mora se definisati prijateljska funkcija u klasi drugog argumenta. • Primjer: complex operator-(double d, complex c) mora biti prijateljska, a ne članica. • Operatorska funkcija članica ne dozvoljava konverziju lijevog operanda.
Unarni i binarni operatori • Unarni operator ima samo jedan operand, pa se može realizovati: • kao operatorska funkcija članica bez argumenata: tip operator@ () • kao globalna funkcija sa jednim argumentom: tip operator@ (X x) • Binarni operator ima dva argumenta, pa se može realizovati: • kao funkcija članica sa jednim argumentom: tip operator@ (X xdesni) • kao globalna funkcija sa dva argumenta: tip operator@ (X xlevi, X xdesni)
Preklapanje unarnih operatora • Unarni operator može biti preklopljen kao: • nestatička funkcija članica bez argumenata; ne može biti statička jer mora pristupati pojedinačnom atributu • samostalna funkcija (nije funkcija članica) sa jednim argumentom (objekat ili referenca)
Preklapanje binarnih operatora • Binarni operator može biti preklopljen kao: • nestatička funkcija članica sa jednim argumentom • samostalna funkcija sa dva argumenta (bar jedan mora biti objekat ili referenca na objekat)
Izbor povratnih tipova operatorskih funkcija • operatori koji mijenjaju lijevi operand (npr. =) treba da vrate referencu na lijevi operand • operatori koji biraju ili isporučuju operande (npr. [], ->, ()) obično treba da vrate referencu na izabranu vrednost • operatori koji izračunavaju novu vrijednost, a ne mijenjaju svoje operande (operatori nad bitovima, +, -, *, /, %, &, \, ^, &&, ||, unarni (-, +), !~) obično treba da vrate kopiju lokalno kreiranog objekta • preinkrement i predekrement - obično treba da vrate referencu na operand • postinkrement i postdekrement - obično treba da sačuvaju kopiju svog operanda, izmijene operand i zatim vrate kopiju
Preklapanje operatora ++ i -- • Operatori inkrementiranja i dekrementiranja imaju: • prefiksni oblik: ++objekat --objekat • postfiksni oblik: objekat++ objekat-- • Da bi se omogućilo preklapanje operatora ++ za oba oblika, moramo imatirazličite definicije operatorskih funkcija za prefiksno odnosno postfiksno inkrementiranje.
Primjer1 #include <iostream.h> class Complex { double real, imag; public: friend ostream & operator<< (ostream &dat, const Complex &z) { return dat << "(" << z.real << "," << z.imag << ")" ; } Complex (double re=0, double im=0) : real(re), imag(im) {} Complex & operator++ () { ++real; return *this; } Complex operator++ (int i) { Complex t=*this; real++; return t; } Complex & operator-- () { --real; return *this; } Complex operator-- (int i) { Complex t=*this; real--; return t; } };
Primjer2 #include <iostream.h> class Complex { double real, imag; public: friend ostream & operator<< (ostream &dat, const Complex &z) { return dat << "(" << z.real << "," << z.imag << ")" ; } Complex (double re=0, double im=0) : real(re), imag(im) {} friend Complex & operator++ ( Complex &z) { z.real++; return z; } friend Complex operator++ ( Complex &z, int i) { Complex t=z; z.real++; return t; } friend Complex & operator-- ( Complex &z) { z.real--; return z; } friend Complex operator-- ( Complex &z, int i) { Complex t=z; z.real--; return t; } };
Preklapanje operatora << i >> • U C++ ne postoje naredbe niti operatori za ulaz i izlaz podataka, već se ulaz i izlaz realizujuodgovarajućim klasama. • Svakoj otvorenoj datoteci, kao i standardnom ulazu/izlazu pridružuje se po primjerak jedne odtih klasa. • Pristup nekoj datoteci ostvaruje se pristupanjem tim objektima putem njihovihfunkcija članica ili prijateljskih funkcija. • Sve potrebne deklaracije vezane za klase za ulaz i izlaz podataka nalaze se u <iostream.h> • Klasa za ulaz podataka naziva se istream. Klasa za izlaz podataka naziva se ostream.
Preklapanje operatora << i >> • Objekat klase istream za pristup standardnom ulazu (tastaturi) zove se cin. • Objekat klase ostream za pristup standardnom izlazu (monitor) zove se cout. • Ova dva objekta (cin, cout) automatski se stvaraju na početku izvršavanja svakog programa. • Operatori za čitanje i pisanje podataka (uz primjenu ulazno-izlaznih konverzija) definisani su zasve standardne tipove podataka preklapanjem operatora >> i <<.
Preklapanje operatora << i >> • Prototipovi odgovarajućih operatorskih funkcija su: • istream & operator>> (istream &dat, Tip &data); • ostream & operator<< (ostream &dat, const Tip &data); • Obje funkcije vraćaju upućivač na dat (cin i cout). • Ovi upućivači su ujedno iprvi operandi operatorskih funkcija. • Ovo omogućava kaskadno pozivanje operatora>> i <<, odnosno prenošenje više podataka jednim izrazom. Ovo je moguće jer je njihovaasocijativnost slijeva udesno.
Preklapanje operatora << i >> • Programer može preklapanjem operatora >> i << da obezbijedi operatore za U/I konverzijesvojih klasa. • Ove funkcije moraju da budu prijateljske funkcije korisničkim klasama. • Ne moguda budu funkcije članice, jer prvi objekat nije korisnički definisan.
Primjer #include <iostream.h> class Complex { double real, imag; friend ostream & operator<< (ostream &dat, const Complex &z) { return dat << "(" << z.real << "," << z.imag << ")" ; } public: Complex (double re=0, double im=0) : real(re), imag(im) {} }; main() { Complex z(1,1); cout << "z = " << z << endl; }
Preklapanje operatora za konverziju tipa (tip) • Konverzija podataka iz jednog tipa u neki drugi tip u slučajevima • dodjele vrijednosti • računanja pri različitim tipovima operanada • prenošenja vrijednosti u funkciju • vraćanja vrijednosti iz funkcije • Kompajler zna kako se vrše konverzije između standardnih tipova (konverzije ugrađene u sam jezik) • Operacija konverzije obično se nazivakastovanje (cast).
Preklapanje operatora za konverziju tipa (tip) • Kod korisnički definisanih podataka kompajler ne zna kako se vrše konverzije, pa programer mora to posebno da definiše. • Moguće realizacije: • pomoću konstruktora konverzije - konstruktor sa jednim argumentom određenog tipakoji prilikom kreiranja objekta vrši konverziju argumenta. • pomoću operatora konverzije (cast operator), mora biti nestatička funkcijačlanica.
Preklapanje operatora za konverziju tipa (tip) • Operatorska funkcija, koja vrši konverziju u tip (klasu) T, ima oblik • operator T() • ne navodi se tip koji vraća, jer se podrazumijeva da je to upravotip u koji treba biti konvertovan objekat.
Primjer class Kompl { double re, im; public: Kompl (double r=0, double i=0) { re=r; im=i; } // Inicijalizacija ili konverzija. operator double(){ return sqrt(re*re+im*im); } // Konverzija u double. friend double real (Kompl z) { return z.re; } // Realni dio. friend double imag (Kompl z) { return z.im; } // Imaginarni dio. Kompl operator+ (Kompl z) // Sabiranje. { z.re += re; z.im += im; return z; } friend ostream& operator<< (ostream& it, const Kompl& z) { return it << '(' << z.re << ',' << z.im << ')'; } }; int main () { Kompl a (1, 2); cout << "a = " << a << endl; Kompl b = a; cout << "b = " << b << endl; Kompl c = 5; cout << "c = " << c << endl; cout << "a+b = " << a + b << endl; cout << "a+3 = " << a + (Kompl)3 << endl; cout << "|a|+3 = " << (double)a + 3 << endl; cout << "a+(3,4) = " << a + Kompl (3,4) << endl; cout << "dble(a) = " << (double)a << endl; double d=Kompl(3,4); cout << "d = " << d << endl; }
Preklapanje operatora = • Ovaj operator ima automatsko tumačenje za objekte klasnih tipova, kopiranje izvorišnog u odredišni objekat, polje po polje • Ovakvo ponašanje je zadovoljavajuće samo za jednostavne objekte • Kod složenih objekata potrebno je izvršiti preklapanje operatora = da bi se izvršilo kopiranje kompletnog objekta • Operator = može biti preklopljen samo nestatičkim metodama klasa, ne može biti globalna funkcija izvan klase
Primjer class Tekst { char* txt; // Pokazivac na sam tekst. public: Tekst (const char* niz) { // Konverzija iz char* u Tekst. txt = new char [strlen(niz)+1]; strcpy (txt, niz); } Tekst (const Tekst& tks) { // Inicijalizacija Tekst-om. txt = new char [strlen(tks.txt)+1]; strcpy (txt, tks.txt); } Tekst& operator= (const Tekst& tks) { // Dodela vrednosti. if (this != &tks) { delete [] txt; txt = new char [strlen(tks.txt)+1]; strcpy (txt, tks.txt); } return *this; } ~Tekst () { delete [] txt; } }; int main () { Tekst a ("Dobar dan."); // Stvaranje i inicijalizacija. Tekst b = Tekst ("Zdravo."); Tekst c (a), d = b; a = b; // Dodela vrednosti. }
Preklapanje operatoraza indeksiranje [] • Binarni operator, odgovara mu operatorska funkcija • tip_rezultataoperator[] (indeks) • Pristup elementu niza niz[ind] odgovara izrazu • niz.operator[] (ind) • Preklapanje operatora [] u nekoj klasi omogućava korišćenje objekata izrazima oblika • obj[ind] • Za razliku od standardnog indeksiranja (indeksni izraz je cjelobrojan), kod preklopljenog operatora [] tip argumenta ind može da bude bilo kojeg tipa.
Preklapanje operatoraza indeksiranje [] • Moguće primjene kod objektnih nizova kod kojih se elementima (objektima) ne pristupa na bazirednog broja (indeksa) nego na bazi neke vrijednosti (ključa). • Da bi izraz sa preklopljenim operatorom [] mogao da se koristi u izrazima oblika obj[ind]=izraz, tip vrijednosti operatorske funkcije operator[] () mora biti upućivač na objekte posmatrane klase. • Operatorska funkcija djeluje samo na objekat svoje klase, a ne na nizobjekata. • Za preklapanje operatora indeksiranja mogu da se koriste samo nestatičke metode.
Preklapanje operatora za pozivanje funkcija() • Binarni operator, odgovara mu operatorska funkcija • tip_rezultataoperator() (arg1, arg2, ..., argN) • Pozivanje funkcije f(arg1, arg2, ... , argN) odgovara izrazu • f.operator() (arg1, arg2, ... , argN) • Preklapanje operatora () u nekoj klasi omogućava korišćenje objekata izrazima oblika • obj(arg1, arg2, ... , argN) • Preklapanje samo nestatičkim metodama