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:
[email protected] 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
[email protected] 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) { a = 10; } int x = 20; akarmi(x);
// érték szerinti paraméterátadás
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 - 16 -
OOP_14
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 ! // új string létrehozása !!!
a = ”Jancsi”; } string x = ”Juliska”; akarmi(x);
Console.WriteLine(x.nev); // ez még mindig Juliska string a = ”Jancsi”; a = a.ToUpper();
void akarmi(string a)
// ennek során valójában új string jön létre, // és a régi megszűnik (garbage collector !) // érték szerinti paraméterátadás, de ref ! - 17 -
OOP_14
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 - 18 -
OOP_14