Transcript 03-PreklapanjeOperatora
Preklapanje operatora
Tipovi operatora Tipovi podataka i efekat operatora Pojam preklapanja operatora Operatorske funkcije Konverzije tipova
Korišćenjem operatora Matrix a, b, c; Matrix d = c * (a + b); Bez operatora, korišćenjem metoda Matrix a, b, c; Matrix d = c.Multiply(a.Add(b));
◦ ◦ Dvije kategorije tipova Value types Prosti tipovi (int, byte, float...) i strukture Direktno sadrže vrijednost Vrijednost se čuva u stack dijelu memorije Reference types Klasni tipovi Sadrže referencu na vrijednost (adresu memorijske lokacije) Vrijednost se čuva u managed heap dijelu memorije Zavisno od kategorije tipa, razlikuje se način izvršavanja operacija nad podacima tog tipa
Type
Value Type Reference Type
Value Types Built-in Type int float User-Defined enum struct
// i, j su tipa int i = 20; j = i; i++; j+=2; Console.WriteLine(i); Console.WriteLine(j); Koje vrijednosti se ispisuju?
Kreiranje varijabli coordinate c1; c1 = new coordinate(); c1.x = 6.12; c1.y = 4.2; Uklanjanje reference c1 = null; • • 6.12 4.2
6.12 4.2
c1 c2 • • 2.3 7.6
coordinate c1= new coordinate( ); coordinate c2; c1.x = 2.3; c1.y = 7.6; c2 = c1; Console.WriteLine(c1.x + " , " + c1.y); Console.WriteLine(c2.x + " , " + c2.y);
◦ Poređenje Value tipova Operatori “==“ i “!=“ porede vrijednosti ◦ Poređenje Reference tipova Operatori “==“ i “!=“ porede reference, ne porede vrijednosti • 1.0 2.0
Dvije različite reference na iste vrijednosti • 1.0 2.0
//Vektor je klasa koja posjeduje javni atribut Vrijednost Vektor x, y; x = new Vektor(); x.Vrijednost = 30; y = x; Console.WriteLine(y.Vrijednost); y. Vrijednost = 50; Console.WriteLine(x. Vrijednost); Koje vrijednosti se ispisuju?
Način poređenja zavisi od tipa objekta, odnosno da li je Value ili Reference tipa Kod varijabli koje su Value tipa (prosti tipovi, instance struktura ili nabrojanja) porede se vrijednosti Kod varijabli koje su Reference tipa (klasni tipovi, instance klasa) porede se reference, odnosno adrese memorijskih lokacija
◦ ◦ ◦ System.Object, nekoliko metoda poređenja ReferenceEquals() Ispituje da li su dvije reference iste, tj da li pokazuju na istu instancu objekta u memoriji Nema smisla da se koristi za Value tipove Equals() Poredi reference za Reference tipove, može se predefinisati za korisničke klase Poredi vrijednosti za Value tipove Operator poređenja (==) Za Reference tipove poredi reference, može se predefinisati Za Value tipove poredi vrijednosti, potrebno ga je redefinisati za korisničke tipove (strukture)
◦ ◦ 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 operatori prilagode tako da izvršavaju određenu akciju nad objektima klasnih tipova
Moguće je izvršiti preklapanje skoro svih operatora Preklapanje operatora treba pažljivo koristiti, zbog mogućih neželjenih efekata Primjeri korištenja preklapanja operatora kod ugrađenih tipova ◦ operator dodjele se koristi na isti način za različite tipove podataka ◦ operator sabiranja se koristi i za cijele i za realne brojeve
Za korištenje operatora nad objektima klasnih tipova u opštem slučaju potrebno je preklapanje operatora Preklapanje se vrši tako što se definiše operatorska funkcija Operatorske funkcije se implementiraju: ◦ kao metode ◦ kao globalne funkcije, obično prijateljske funkcije klasa (C++)
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 jezicima C#/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 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):
class
complex {
public
: complex(
double
,
double
); /* konstruktor */
friend
complex
operator
+(complex,complex); /* oparator + */
friend
complex
operator
-(complex,complex); /* operator - */
private
:
double
real, imag; }; complex::complex (
double
r,
double
i) : real(r), imag(i) {} complex
operator
+ (complex c1, complex c2) { complex temp(0,0); // privremena promenljiva tipa complex temp.real=c1.real+c2.real; temp.imag=c1.imag+c2.imag;
return
temp; } complex
operator
- (complex c1, complex c2) {
return
complex(c1.real-c2.real,c1.imag-c2.imag); //poziv konstruktora }
} { struct Vector { public double x, y, z; public Vector(Vector rhs) x = rhs.x; y = rhs.y; z = rhs.z; { } public static Vector operator + (Vector lhs, Vector rhs) Vector result = new Vector(lhs); result.x += rhs.x; result.y += rhs.y; result.z += rhs.z; return result; }
C#/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.
Postoje neka ograničenja u preklapanju operatora: ◦ ne mogu direktno da se preklope svi operatori, neki ne mogu nikako; ◦ ◦ ◦ 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: asocijativnost.
n -arnost, prioritet i
Ako je @ neki binarni operator (npr +), on može da se realizuje: ◦ ◦ Kao funkcija članica klase X Kao prijateljska globalna funkcija Nije dozvoljeno da se u programu nalaze obje ove funkcije
class
complex {
double
real,imag;
public
: complex (
double
r=0,
double
i=0) : real(r), imag(i) {} complex
operator
+(complex c) {
return
complex(real+c.real,imag+c.imag; } }; // ili, alternativno:
class
complex {
double
real,imag;
public
: complex (
double
r=0,
double
i=0) : real(r), imag(i) {}
friend
complex
operator
+(complex,complex); }; complex
operator
+ (complex c1, complex c2) {
return
complex(c1.real+c2.real,c1.imag+c2.imag); }
void
} main () { complex c1(2,3),c2(3.4); complex c3=c1+c2; // poziva se c1.operator+(c2) ili operator+(c1,c2)
Unarni operator ima samo jedan operand, pa se može realizovati: ◦ kao operatorska funkcija članica bez argumenata: tip operator@ () ◦ Binarni operator ima dva argumenta, pa se može realizovati: ◦ kao funkcija članica sa jednim argumentom: tip operator@ (X xdesni) ◦ kao globalna funkcija sa jednim argumentom: tip operator@ (X x) kao globalna funkcija sa dva argumenta: tip operator@ (X xlevi, X xdesni)
◦ ◦ 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)
◦ ◦ 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)
class
complex { //...
public
: complex
operator
!();
friend
complex
operator~
(complex); complex
operator-
(complex); // članica unarni operator! // globalna f-ja, unarni operator~ // članica binarni operator-
friend
complex
operator
+(complex,complex); // globalna f-ja, binarni operator+ };
◦ ◦ ◦ Operatorske funkcije realizuju se kao funkcije članice klasa Operatorske funkcije se definišu kao javne i statičke metode klasa Ne mogu koristiti pokazivač this na objekat date klase Imaju jedan argument za unarne, dva za binarne operatore Najmanje jedan argument mora biti korisnički definisan tip (klasa, struktura)
} { public static Vector operator + (Vector lhs, Vector rhs) Vector result = new Vector(lhs); result.x += rhs.x; result.y += rhs.y; result.z += rhs.z; return result; } { public static Vector operator * (double lhs, Vector rhs) return new Vector(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); } { public static double operator * (Vector lhs, Vector rhs) return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
Postoji 6 relacionih operatora u jeziku C# i moraju se preklapati u parovima ◦ ◦ ◦ == i != > i < >= i <= Ako se izvrši preklapanje operatora ==, mora se preklopiti i operator !=, u protivnom kompajler prijavljuje grešku Slično važi za ostale relacione operatore Relacioni operatori vraćaju logičku vrijednost
Ako se izvrši preklapanje operatora == i !=, moraju se predefinisati i funkcije
GetHashCode()
Funkcija
Equals() Equals()
i naslijeđene iz System.Object
treba da implementira istu logiku kao i operator jednakosti Ako se ne izvršti redefinisanje prethodne dvije metode, kompajler generiše upozorenje (ne grešku)
} { public static bool operator == (Vector lhs, Vector rhs) if (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z) return true; else return false; } { public static bool operator != (Vector lhs, Vector rhs) return ! (lhs == rhs);
◦ ◦ ◦ ◦ ◦ Implicitna konverzija Izvršava se automatski između kompatibilnih tipova upotrijebljenih u izrazima Eksplicitna konverzija Kada postoji rizik gubitka podataka ili pojave greške u konverziji Mora se eksplicitno navesti u izrazu Cast operator ( tip konverziju tipova ) se koristi za eksplicitnu Sintaksa: ( tip ) varijabla int i = 3; long j = i; // implicitna short s = (short)i; // eksplicitna
Konverzija se definiše kao operatorska funkcija u okviru klase Može se definisati kao implicitna ili eksplicitna konverzija Uvijek definisati kao eksplicitnu konverziju u slučaju da postoji bilo kakva mogućnost greške pri konverziji Povratni tip operatorske funkcije jednak je operatoru konverzije } { public static implicit operator float (Currency value) return value.Dollars + (value.Cents/100.0f);
//primjer definisanja operatora konverzije za slučaj struktura //na isti način se radi sa klasama { struct Currency public uint Dollars; public ushort Cents; } { public static implicit operator float (Currency value) return value.Dollars + (value.Cents/100.0f); } ...
Currency balance = new Currency(10,50); float f = balance; //implicitna konverzija Currency u float
{ struct Currency public uint Dollars; public ushort Cents; } } { public static explicit operator Currency (float value) uint dollars = (uint)value; ushort cents = (ushort)((value-dollars)*100); return new Currency(dollars, cents); } ....
float amount = 45.63f; Currency amount2 = (Currency)amount; //eksplicitna konverzija Currency amount3 = amount; // greška
◦ ◦ Moguće je definisati konverzije između bilo koja dva korisnički definisana tipa (klase, strukture) Ograničenja Ne može se definisati konverzija za dvije klase ako je jedna izvedena iz druge klase Konverzija se mora definisati u okviru definicije izvorišne ili odredišne klase (tipa)
Moguće je definisati konverziju samo između klasa C i D Operatorska funkcija za konverziju se definiše u okviru definicije klase C ili D, ne u obje Mora biti moguć pristup source kodu klasa C ili D da bi se realizovala konverzija Ograničenja Hijerarhija klasa
} { public static explicit operator D(C value) //implementacija konverzije } { public static explicit operator C(D value) //implementacija konverzije