OOP #14 (referencia-elv) v1.0 2003.03.19. 21:22:00 Eszterházy Károly Főiskola Információtechnológia tsz. Hernyák Zoltán adj. e-mail: aroan@ektf.hu web: http://aries.ektf.hu/~aroan OOP OOP_14-1 -
E jegyzet másolata nem használható fel szabadon, az előadás anyagának kivonata. Ezen teljes jegyzetről, vagy annak bármely részéről bármely másolat készítéséhez a szerző előzetes írásbeli hozzájárulására van szükség. A másolatnak tartalmaznia kell a sokszorosításra vonatkozó korlátozó kitételt is. A jegyzet kizárólag főiskolai oktatási vagy tanulmányi célra használható! A szerző hozzájárulását adja ahhoz, hogy az EKF számítástechnika tanári, és programozó matematikus szakján, a tárgyat az EKF TO által elfogadott módon felvett hallgatók bármelyike, kizárólag saját maga részére, tanulmányaihoz egyetlen egy példány másolatot készítsen a jegyzetből. A jegyzet e változata még tartalmazhat mind gépelési, mind helyességi hibákat. Az állítások nem mindegyike lett tesztelve teljes körűen. Minden észrevételt, amely valamilyen hibára vonatkozik, örömmel fogadok. Hernyák Zoltán aroan@ektf.hu OOP OOP_14-2 -
Referencia-elv Az objektum-példány definiálása önmagában nem jelent memóriafoglalást. A példányhoz tartozó memóriafoglalás a new operátor hívásakor valósul meg. A new nem a példány alaptípusának foglal helyet, hanem a konstruktor típusa dönti el a memóriafoglalást! A new -al kötelező megadni egy konstruktor-t is. A példány e pillanattól kezdve ezen memóriaterületre vonatkozó referenciát (hivatkozást) tartalmaz. Ez nagyon hasonlít egy pointerre. Referencia-elv OOP_14-3 -
A különbség az, hogy az objektumhoz tartozó memóriaterületet az operációs rendszer elmozgathatja, a referencia azonban továbbra is azonosítja a területet. A pointer a memória kezdőcímét tartalmazza, ha az operációs rendszer elmozgatja a foglalt területet, a pointer már rossz helyre mutatna. Ha két példány között értékadás műveletet hajtunk végre, akkor a referencia másolódik csak át: TMasodik m1 = new TElso(); TMasodik m2 = m1; Referencia-elv OOP_14-4 -
Ugyanez történik objektumokkal kapcsolatos paraméterátadásakor is! void Berak(TMasodik m) { m.szam = 20; } TMasodik x = new TMasodik(); x.szam = 10; Berak( x ); Console.WriteLine( x.szam ); // 20!!!! Az objektum típusú paraméterek ezért akkor is ref típusú paraméterek, ha azt nem jelöljük a paraméter-módosítóval. Referencia-elv OOP_14-5 -
Null-referencia A példányok valójában referencia-típusúak (reference type) TMasodik m; Ez egy m nevű referenciát definiál, mely 4 byte-ot foglal el, és induláskor még nem tartalmaz referenciát egyetlen memóriaterületre sem: A nem létező referencia a null konstans. Pl: if (e==null) Console.WriteLine( A példány még nem létezik ); Null-referencia OOP_14-6 -
Referencia és típuskompatibilitás TMasodik m = new TMasodik (); TElso e = m; // rendben, típuskompatibilis e.szam = 10; Console.WriteLine( m.szam ); // 10-et ír ki e használható, de mivel az ő alaptípusa TElso, a fordító úgy tekint rá, mintha ő valódi TElso lenne, ezért e -nek csak olyan mezői és metódusai vannak (szerinte), amelyek a TElso-ben vannak definiálva. e ugyanaz mint az m, de visszabutítva TElso szintre, vagyis e mezőinek az értéke ugyanaz, mint m -nek, de a korai kötések a TElso szerint működnek Referencia és típuskompatibilitás OOP_14-7 -
Az e VMT-je a TMasodik-é, ezért a késői kötések a TMasodik szerint működnek! TElso e; // nem jön létre még a változó TMasodik temp = new TMasodik(); e = temp; Összevonva egy sorba: TElso e = new TMasodik(); //!!!!! jó OOP_14-8 -
Minden olyan változó referencia elven van kezelve, amelynek típusa class. Ez nem minden esetben jó! int a = 10; int b = a; a = 20; // b=20 szintén!?! nem!!! A nyelv építőkő jellegű alaptípusai nem lehetnek referencia-elvűek. Ezen típusok érték típusú (value type) adatok. Értékadáskor csak a bennük tárolt érték másolódik át. Az OOP nyelveken nem lehet olyan típus, amelyik nem OOP típus! Minden olyan változó referencia elven van kezelve, amelynek típusa class. OOP_14-9 -
Ezért a nyelvek kétfajta objektum-típust támogatnak (kétféle módon lehet osztályt definiálni): A class kulcsszóval Y referencia elvű példány A struct kulcsszóval Y érték típusú példány Az enumeration típusú változók is érték típusúak Ezért a nyelvek kétfajta objektum-típust támogatnak (kétféle módon lehet osztályt definiálni): OOP_14-10 -
Ha C#-ban a struct-al definiálunk egy példányt Nem kell a new kulcsszót használni a példányosításhoz Ha nem használjuk a new kulcsszót, akkor a struktúra mezőinek nem lesz kezdőértéke (memóriaszemét) Ilyen osztály mezőire nem használhatunk kezdőértékadást. Ezért a struct-nak általában van konstruktora A paraméter nélküli (default) konstruktort a nyelv automatikusan készíti. Mi csak paraméteres konstruktort készíthetünk! A struct típus korlátozott OOP tulajdonságokkal rendelkezik, pl. nem működik rá az öröklődés semmilyen formában (egy struct nem lehet ős, ő nem lehet gyermek) Ugyanakkor a struct őse is az Object Viszont implementálhat interface-t A példányosítás kevesebb memóriát igényel, mert a példány nem kerül +4 byte-ba a referencia tárolása miatt. Ha C#-ban a struct-al definiálunk egy példányt OOP_14-11 -
A nyelv alaptípusai (int, double, boolean, char, ) mind struct típusúak, hogy a kifejezésekben a szokott módon használható legyenek. Amíg a struktúra mezői nincsenek feltöltve, addig nem használható Ezért az int a; deklaráció után az a változó még nem használható fel. Az int a=0; után már igen! Ez akár az alábbi formában is írható: int a = new int(); OOP_14-12 -
Referencia-elv előnyei: garbage collector működését támogatja nem pointer, ezért a memóriaterület áthelyezhető (windows memóriakezelés mellett ez sűrűn előfordul ) Hátrányai: Nem minden esetben egyértelmű a viselkedés Referencia-elv előnyei: OOP_14-13 -
Pl: class THallgato { public string nev; public int[] jegyek=new int[20]; } class IskolaiOsztaly { private System.Collections.ArrayList tanulok; public THallgato this[int index] { get { return (THallgato)tanulok[index]; } } } Ebben az esetben azt hihetnénk, hogy a csak olvasható propertyben visszaadott THallgato is csak olvasható lesz. De nem! Pl: OOP_14-14 -
int[] vektor = new int[10]; foreach(int a in vektor) // a változó felveszi a megfelelő értéket { // a értéke csak olvasható a = 10; //hibás // // mert ha beleírnánk, az nem a megfelelő } // tömbelem értékének átírását jelentené THallgato[] vektor = new THallgato[10]; foreach(thallgato a in vektor) // az a értéke itt sem írható { // de az a által referált objektum a.nev = ismeretlen ; // mezői átírhatóak, vagy meghívható- } // ak a metódusai OOP_14-15 -
void akarmi(int a) // érték szerinti paraméterátadás { a = 10; } int x = 20; akarmi(x); Console.WriteLine(x); // még mindig 20 void akarmi(thallgato a) // érték szerinti paraméterátadás, de ref! { a.nev = Jancsi ; } THallgato x = new THallgato(); x.nev = Juliska ; akarmi(x); Console.WriteLine(x.nev); // ez már Jancsi void akarmi(int a) // érték szerinti paraméterátadás OOP_14-16 -
A string típus azonban referencia típusú, de az értékadás mindig új string létrehozását jelenti! void akarmi(string a) // érték szerinti paraméterátadás, de ref! { a = Jancsi ; // új string létrehozása!!! } string x = Juliska ; akarmi(x); Console.WriteLine(x.nev); // ez még mindig Juliska string a = Jancsi ; a = a.toupper(); // ennek során valójában új string jön létre, // és a régi megszűnik (garbage collector!) void akarmi(string a) // érték szerinti paraméterátadás, de ref! OOP_14-17 -
a = a+ és Juliska ; // ennek során új string jön létre, amely értéke // a Jancsi és Juliska, és a régi string megszűnik string a = Jancsi ; Console.WriteLine(a+ és Juliska ); A fv hívásakor (röptében) létrejön egy string, amely tartalmazza az összefűzött szöveget, az a továbbra is csak a Jancsi szöveget tartalmazza a fv futása után is (nem változott a referencia). string a = Jancsi ; string b = és ; string c = Juliska ; string x = a + + b + + c; // mindig új string-ek jönnek (4 esetben) a = a+ és Juliska ; // ennek során új string jön létre, amely értéke OOP_14-18 -