Az előadás letöltése folymat van. Kérjük, várjon

Az előadás letöltése folymat van. Kérjük, várjon

Absztrakt adattípusok kAkAÓ 2004. Objektumorientált programozás. In: Nyékyné Gaizler Judit (szerk.): Programozási nyelvek, Kiskapu, Budapest, 2003. pp.333-350.

Hasonló előadás


Az előadások a következő témára: "Absztrakt adattípusok kAkAÓ 2004. Objektumorientált programozás. In: Nyékyné Gaizler Judit (szerk.): Programozási nyelvek, Kiskapu, Budapest, 2003. pp.333-350."— Előadás másolata:

1 Absztrakt adattípusok kAkAÓ 2004. Objektumorientált programozás. In: Nyékyné Gaizler Judit (szerk.): Programozási nyelvek, Kiskapu, Budapest, 2003. pp.333-350.

2 kAkAÓ2 A programozási nyelvek fontos feladata a használt programozási módszertanok aktív támogatása. Elvárások a programnyelvekkel szemben: 1. Modularitás: az egyes típusokat önálló fordítási egységekben lehessen megvalósítani. Ezek legyenek újrafelhasználhatóak, fejleszthesse más- más ember őket. 2. Egységbezárás (encapsulation): a nyelv kezelje összetartozó egységként a típusértékhalmazt és műveleteit. 3. Az ábrázolás elrejtése: az adott típus használója csak a specifikációban megadott tulajdonságokat használhatja ki. Ezért lehet változtatásokat végrehajtani úgy, hogy az ne érintse a hierarchia más lépcsőit. 4. A specifikáció és az implementáció szétválasztása külön fordítási egységbe lehetővé teszi, hogy az adott típust használó más modulok a típusspecifikáció birtokában elkészíthetők, függetlenül a megvalósítástól.

3 kAkAÓ3 5. A modulfüggőség kezelése: a fordítóprogram maga kezelje a modulok közötti kapcsolatokat (egyik használja a másikat stb.) 6. Konzisztens használhatóság: a felhasználói és beépített típusok ne különbözzenek a használat szempontjából. 7. Általánosított programsémák támogatása: a programozó minél általánosabban írhassa meg programjait. A nyelv adjon lehetőséget az ismétlések minimalizálására, mert ez javítja a kód olvashatóságát és karbantarthatóságát.

4 kAkAÓ4 Modularitás. A modern nyelvekben a modulokra bontás alapja a típusokra bontás, azaz egy modul egy típust valósít meg. A modulok határai gyakran a láthatósági határokat is jelentik, gyakran a modulok kifelé korlátozzák a bennük definiált adatok láthatóságát. Egységbezárás. Lényege, hogy a típusokat a rajtuk értelmezett műveletekkel együtt, egységként használja. Azok az az alprogramok jogosultak egy típus belső szerkezetéhez hozzáférni, amelyek a típus műveleteit valósítják meg, azaz részei az egységbezárásnak. Az ún. öröklődés során a származtatott típus örökli az őstípus primitív műveleteit (ezek azok, amelyek a típussal „egységbe vannak zárva”). A C++-ban például ezek a műveletek az osztály névterében definiált műveletek.

5 kAkAÓ5 Az ábrázolás elrejtésének támogatására a nyelvek az összetett típusok komponenseinek láthatóságát gyakorta szintekre bontják, amely szintek meghatározzák, hogy pontosan kik férhetnek hozzá az adott komponenshez. Persze nem minden nyelvben: például C, Pascal vagy Modula nem rendelkezik ilyen szabályozással. Általában 3 szintet szoktak az OOP nyelvekben megkülönböztetni: Nyilvános (public) – az adott komponens mindenki számára hozzáférhető Védett (protected) – az adott komponens csak a leszármazottak számára látható (csak az öröklődést támogató nyelvekben) Privát (private) – az ábrázolás teljesen rejtett része, csak a műveletek megvalósításában használható komponensek. Ezt a 3 szintet használja pl. az ADA 95 vagy a C++, a Java és az Eiffel több szintet tesz lehetővé.

6 kAkAÓ6 A típusspecifikáció és a megvalósítás szétválasztása külön fordítási egységbe az alábbi előnyökkel jár:  Segít abban, hogy az egyes modulok egymástól függetlenül elkészíthetőek legyenek,  A típus használatához szükséges összes információ elkülönülten is rendelkezésre áll, így a megvalósítás lefordított állapotban is szállítható. Ha a specifikációt és az implementációt két fordítási egységre bontjuk szét, gondoskodni kell arról, hogy csak a specifikációs rész birtokában a fordító program képes legyen a szükséges memóriafoglalást meghatározni. C és C++ fejállományok (header fájlok) A C/C++ nyelvekben a specifikációt fizikailag be kell másolni minden olyan fordítási egysége, amely az adott típust használni akarja. A specifikáció egy külön speciális forrásfájlban (header fájl) leírható, és ezt az előfordító emeli be a megfelelő fordítási egységbe.

7 kAkAÓ7 A programok legmagasabb szintű építőkövei a modulok. A programok működése ezen modulok interakciója. Minden modul igényel szolgáltatásokat és segítségükkel más szolgáltatásokat valósít meg. A modulok között egyfajta függőség alakul ki, például egy megváltozott modul miatt más modulok újrafordítása is szükségessé lehet. C++-ban ennek a kezelése a programozó feladata: minden fordítási egységet a programozónak külön kell kezelnie. Erre való a make. Maga a nyelv azonban nem nyújt támogatást. Az ADA nyelvben a with segítségével meg lehet adni, hogy a szóbanforgó modul milyen más fordítási egységektől függ. Így a modul lefordítása előtt mindazon modulok specifikációs részének lefordítására sor kerül – ha szükséges – amelyektől ez a modul függ.

8 kAkAÓ8 Azonosító túlterhelése (overloading). Egy azonosító túlterhelése azt jelenti, hogy a programszöveg egy adott pontján az azonosítónak több definíciója is érvényben van. Ilyenkor a fordító a hivatkozási környezetből dönti el, hogy a több lehetséges értelem közül melyiknek megfelelően használja az adott azonosítót. Példa: a + operátor mást jelöl a 3+4 illetve a 3.13+4.2 kifejezésekben. A fordítóprogram ilyenkor az operandusok típusa alapján határozza meg, hogy a + műveletnek mit kell csinálnia. Csinálhat-e ilyet a programozó is? Azaz lehetséges-e, hogy egy, a programozó által definiált azonosító egyszerre több definícióval bírjon, melyek közül a fordító az argumentumok típusa alapján választja ki a ténylegesen végrehajtandó példányt. Nem számít túlterhelésnek, ha egy leszármaztatott osztály megváltoztatja egy ősosztályban deklarált művelet definícióját: ezt felüldefiniálásnak nevezzük. Ebben az esetben a hivatkozáshoz tartozó tényleges definíció nem fordítási időben (statikusan), hanem futási időben (dinamikusan) választódik ki. Ezt a jelenséget dinamikus összekapcsolásnak nevezzük (late binding).

9 kAkAÓ9 A túlterhelés speciális esete az operátor túlterhelés, amikor egy nyelv operátorait (+, –, *, /, =,, sizeof, new stb.) terheljük túl. Ha pl. definiálunk egy mátrix típust, szeretnénk a mátrixok szorzását is *-gal jelölni.

10 Típussal való paraméterezés kAkAÓ 2004. Objektumorientált programozás. In: Nyékyné Gaizler Judit (szerk.): Programozási nyelvek, Kiskapu, Budapest, 2003. pp.353-388.

11 kAkAÓ11 A programfejlesztés során gyakran merül fel annak igénye, hogy általánosan megfogalmazott, széles körben alkalmazható megoldásokat adjunk. Megfelelő paraméterek megválasztásával az alprogramok erre lehetőséget adnak. Az alprogramok paraméterei nem lehetnek típusok, pedig a különféle típusokon megadott hasonló feladatokat hasznos lenne egyetlen alprogrammal megoldani. A típusparaméterrel ellátott alprogramokat polimorfnak, azaz többalakúnak nevezzük. Előfordulhat az is, hogy a különböző típusokhoz különböző implementáció tartozik. Példa: különböző alaptípusú tömök rendezése egyetlen rendező alprogrammal, amely az elemtípussal van paraméterezve.

12 kAkAÓ12 template void rendez(vector & v){ const size_t n=v.size(); for (int gap=n/2; 0<gap; gap /= 2;{ for (int i=gap; i<n; i++){ for (int j=i – gap; 0<=j; j – =gap){ if (v[j+gap]<v[j]){ Elem temp=v[j]; v[j]=v[j+gap]; v[j+gap]=temp; } C++-ban a template szerkezettel lehet megoldani a feladatot: Shell rendezés tetszőleges Elem elemtípusú tömbre Elem helyére írhatunk például int-et Ha egész számokat kívánunk rendezni, vector v; rendez(v); kell.

13 Objektumorientált programozás kAkAÓ 2004. Forrás: Objektumorientált programozás. In: Nyékyné Gaizler Judit (szerk.): Programozási nyelvek, Kiskapu, Budapest, 2003. pp.391-456.

14 kAkAÓ14 A számítógépes programok segítségével az ember a valós világot próbálja modellezni. Az objektumorientált szemlélet a valóság megközelítésének és ábrázolásának egy módszere. A modellezés során a valós tárgyakat objektumokkal ábrázolja,, amelyeket állapotukkal (adataikkal) és metódusaikkal jellemez. Az OO megközelítés egy programozási szemléletmód, amelynek alapján rendszerfejlesztési módszertanokat is kidolgoztak. Ezek a módszertanoka teljes fejlesztési folyamatot átfogják: Analízis, Tervezés, Implementálás, Tesztelés, Karbantartás.

15 kAkAÓ15 Az analízis során a rendszert együttműködő objektumok összességeként modellezzük, a tervezés és az implementáció ezen objektumokat alakítjuk ki. Objektumorientáltnak nevezünk egy programot, mely egymással kapcsolatot tartó objektumok összessége, ahol minden objektumnak megvan a jól meghatározott feladata. A modellezés során az ember olyan gondolatmenetet használ, amelynek segítségével elvonatkoztat, megkülönböztet, osztályoz, általánosít, vagy leszűkít, részekre bont, kapcsolatokat épít fel.

16 kAkAÓ16 Az absztrakció az a szemléletmód, amelynek segítségével a valós világot leegyszerűsítjük úgy, hogy csak a cél elérése érdekében feltétlenül szükséges részekre koncentrálunk. Az objektumok a modellezendő valós világ egy-egy önálló egységét jelölik. Meghatározza őket belső állapotuk és a nekik küldhető üzenetekre való reagálásuk. A reagálás során az objektum belső állapota megváltozhat, illetve valamilyen műveletet végez, üzenetet küldhet más objektumoknak. Az objektumokat kategorizáljuk, a hasonlókat egy osztályba soroljuk. Az osztályozás az általánosítás és a specializálás segítségével történik: az objektumok között hasonlóságokat és különbségeket keresünk.

17 kAkAÓ17 Az osztályozás az emberi gondolkodás szerves része. Az ugyanolyan fajta adatokat tartalmazó, és az ugyanolyan viselkedés-leírással (metódusokkal) rendelkező objektumokat egy osztályba soroljuk. Az objektum-osztályok hordozzák a hozzájuk tartozó objektumok jellemzőit. Minden objektum valamilyen osztály példánya (instance), rendelkezik osztályának sajátosságaival, átveszi annak tulajdonságait az adatszerkezetekre és a műveletekre vonatkozóan. Az objektum tehát adatok (attribútumok) és műveletek (metódusok) összessége, ez utóbbiak elvégzik az objektumra szabott feladatot vagy leírják az objektum viselkedését. Attribútum a művészetben: ha egy templomi festményen valaki kulcsot tart a kezében, akkor az Péter apostol, és a művelet a kezében tartás.

18 kAkAÓ18 Az objektumoknak mindig van belső állapotuk – ezt az adatok pillanatnyi értékei jellemzik. Metódushívások után az objektumok állapota meg változhat. Az objektumok emlékeznek állapotukra és a feladatvégzési folyamat mindig egy alapértelmezett kezdőállapotból indul, és egy másik állapotba megy át. A következő állapotátmenetnél onnan folytatja a folyamatot, ahol előzőleg abbahagyta. A kapcsolatban álló objektumok kommunikálnak egymással. Ez üzenetküldés formájában történik. Az üzeneteket kívülről elérhető metódushívásokkal ábrázoljuk. Az üzenetet a megszólított objektum azonosítójával minősítjük, és lehetnek paraméterei: Obj.Üzenet (Paraméterek)

19 kAkAÓ19 Ha az objektumtól valamilyen választ várunk az üzenetre, akkor ezt a változó paramétereken keresztül vagy a metódus visszatérési értékeként kaphatjuk meg. A következőkben megnézzük, hogyan hozhatók létre osztályok és objektumok egyes programozási nyelvekben.

20 kAkAÓ20 SIMULA 67 Class Teglalap(TeglNev, Szelesseg, Magassag); ! Osztály 3 paraméterrel Text TeglNev; Real Szelesseg, Magassag; ! Paraméterek specifikációja Begin Real Terulet, Kerulet; ! Attribútumok; Procedure Frissit; ! Művelet; Begin Terület := Szelesseg * Magassag; Kerület := 2*(Szelesseg + Magassag); End of Frissit; Procedure Kiir; ! Művelet; Begin OutText(”I am a Teglalap”); OutText(TeglNev); OutText(”Terulet: ”); OutFixText(Terulet,2,7); OutText(”Kerulet: ”); OutFixText(Kerulet,2,7); End of Kiir; Frissit; ! Teglalap élete; Kiir; End of Teglalap; Létrehozás: new TeglaLap(„Kicsi”,2,3)

21 kAkAÓ21 SMALLTALK A SMALLTALK-ban minden objektum, még az osztályok is. Ez azt jelenti, hogy az osztályoknak is vannak adattagjaik és metódusaik. Hozzunk létre egy Szamla osztályt egy Egyenleg nevű adattaggal. Object subclass: #Szamla instanceVariableNames: ‘egyenleg’ classVariableNames: ‘’ poolDictionaries: ‘’ Category: nil !

22 kAkAÓ22 C++ -ban egy új osztályt így deklarálhatunk: class Teglalap{ int x, y; public: void ertekadas (int, int); int terulet() { return (x*y); } }; A Teglalap osztály két rejtett adattagja x és y. Két nyilvános metódusa van, az értékadó és a terület számító metódus. A területszámító metódust az osztály deklarációjában definiáltuk. void Teglalap::ertekadas(int x1, int y1) { x = x1; y = y1; } Az értékadást az osztálydeklaráción kívül, a baloldalt látható módon adhatjuk meg.

23 kAkAÓ23 Teglalap haz; // statikus helyfoglalású példány haz.ertekadas(5,3); int thaz = haz.terulet(); // thaz = 15 Teglalap * kert = new Teglalap; // Dinamikus helyfoglalású példány kert ->ertekadas(20,17); int tkert = kert->terulet(); // tkert = 340 A Teglalap osztály egy új tagjának létrehozása C++-ban statikus illetve dinamikus helyfoglalású változóként:

24 kAkAÓ24 Az Object Pascalban egy új osztályt szintén a class kulcsszó segítségével hozhatunk létre: type TDatum = class private Ev, Honap, Nap : integer; public procedure Beallit (e,h,n: integer); function Szokoev: boolean; … end. Az objektum példányok létrehozása dinamikusan történik, ellentétben a Turbo Pascallal, a változó deklarálása után meg kell hívnunk az osztály konstruktorát, amely lefoglalja és inicializálja a szükséges memóriaterületet (Create). A használat befejeztével a memóriaterület felszabadítása is a programozó feladata (Free).

25 kAkAÓ25 Var EgyNap : TDatum; begin … EgyNap := Tdatum.Create; … EgyNap.Beallit(2003,4,6); { használat } … EgyNap.Free; { felszabadítás } end; Az adatmezők és a metódusok mellett az osztálytípusok tulajdonságokat (property) is tartalmazhatnak. A tulajdonság egy olyan név, amely a megadott olvasás és/vagy írás műveleteken keresztül férhet hozzá az objektum adattagjaihoz.

26 kAkAÓ26 Egységes jelölési módok: az osztálydiagram Osztály Adat Adat: típus Adat: típus= érték … metódus metódus(paraméterlista) metódus: típus metódus(paraméterlista):típus … Az osztály neve Adatok metódusok Az osztálydiagram tartalmazza az osztály nevét, az objektumok megvalósítására használt adatokat és típusukat, valamint a metódusokat. Az adatok az állapotok belső leírásai, a metódusok műveletek vagy üzenetek.

27 kAkAÓ27 példányszám Egységes jelölési módok: az objektumdiagram Obj: Osztály adat 1 = érték 1 adat 2 = érték 2 … adat n = érték n Az objektum azonosítója és az osztály neve, amelynek példánya állapot OsztályObjektum példányosítás

28 kAkAÓ28 Az egyes programok objektumaira jellemző az életciklusuk: „megszületnek”, „élnek”, „meghalnak”. Az objektum kezdeti állapotát a konstruktor állítja be. Ekkor történik meg az objektum adattagjainak kezdőértékadása és az objektum működéséhez szükséges kezdeti tevékenységek végrehajtása. A nyilvános osztályok konstruktorának is nyilvánosnak kell lennie, hogy az objektumot tartalmazó program hozzáférhessen. Egyes nyelvekben destruktor is van, melyet az objektum megszűnésekor vagy a programozó, vagy a rendszer meghív és így memóriát szabadít fel.

29 kAkAÓ29 class Komplex { double re, im; public: Komplex() { re=0; im=0;} Komplex(double a, double b) {re=a, re=b;} } …..; Komplex z1; …….. Komplex z2(1,1); C++ példa konstruktorra C++-ban egy osztály konstruktorának neve megegyezik az osztály nevével. Egy osztálynak több konstruktora lehet a függvénynév túlterhelés miatt Az objektumok felszabadítását a destruktor végzi. A destruktor neve az osztálynév elé írt ~(tilde) jellel képezhető. A statikus objektum megszűnésekor illetve a dinamikus objektum helyének felszabadításakor a destruktor automatikusan meghívódik. Paramétere nincs, mivel nem feltétlenül a programozó hívja meg.

30 kAkAÓ30 type Tdatum = class private ev, ho, nap: integer; public constructor Create(e,h,n: integer); … end; constructor Tdatum.Create(e,h,n: integer); Object Pascal Object Pascalban egy osztályhoz több különböző konstruktort hozhatunk létre, ezek neve tetszőleges lehet és akármennyi paraméterük lehet. Deklarálásukkor a constructor kulcsszót kell használni. A TObject osztály (minden osztály őse) konstruktorának neve Create, ezt szokás használni. Object Pascalban a konstruktort az objektumpéldány használata előtt külön meg kell hívni. Az alapértelmezett destruktor a Destroy, akkor híjuk meg, ha már nincs szükségünk az objektumra.

31 kAkAÓ31 Eiffel class KOMPLEX creation ertekadás feature valos, kepzetes: REAL feature ertekadas(r,i: REAL) is do valos := r; kepzetes:=i end; end Eiffelben a konstruktort a creation záradékkal lehet megadni. Az alábbi utasítás hatására létrejön a z-hez rendelt objektum, melynek kezdeti értéket is adunk. z: KOMPLEX; !!z.ertekadas(1,0); Eiffelben nincs explicit destruktor. Amikor egy objektumra már nem hivatkozik többé program, egy automatikus szemétgyűjtő eljárás megszünteti az objektumot (garbage collection)

32 kAkAÓ32 A példányosítás és a Self Delphi: Self; Java, C++: this; A példányosítás során létrejön az objektum és utasítások elvégzésére felszólító üzenetekre vár. A metódusokat az osztályhoz rendeljük és nem másoljuk be minden újonnan létrehozott objektumba: a metódusok az osztály leíró részében szerepelnek csak, az objektumban nem. Az objektumok tudják, hogy melyik osztályhoz tartoznak, és ennek alapján történik a metódusok kiválasztása. Kérdés: ha több objektumot példányosítunk egy osztályból, honnan tudjuk, hogy melyik objektum hívta az adott metódust, azaz a metódusnak melyik objektum adataival kell dolgozni? Erre van egy speciális hivatkozás, amely mindig a metódust meghívó aktuális objektumpéldányra mutat. Ez Delphiben a Self, más nyelvekben this. A Self rámutat arra az objektumra, amellyel a metódusnak dolgoznia kell.

33 kAkAÓ33 procedure TCurveFittingForm.Timer1Timer(Sender: TObject); begin Timer1.Enabled:=False; { <-- stop timer } With StockPrice do Begin Delete(0); { <-- remove the first point } { Add a new random point } AddXY( XValues.Last+1, (YValues.Last/YValues.Multiplier)+(Random(ChartSamplesMax)- (ChartSamplesMax/2)), '',clTeeColor); Chart1Zoom(Self); { <-- recalculate Curve !!!! } end; Timer1.Enabled:=True; { <-- restart timer } end; Delphi példa a Self használatára

34 kAkAÓ34 Megjegyzések a 7 elvárás és az OOP kapcsolatáról Egységbezárás. Számos OOP nyelvben az egységbezárás a korábbi struktúra, ill. rekord adattípusát (struct, record) továbbfejlesztve vezeti be az osztálytípust (class). Ada95-ben az egységbezárás csak közvetetten valósul meg. Az Ada83 rekord fogalmát bővítették ki, de ezen rekordok csak az osztály attributumait tartalmazzák, a műveleteket nem. Így az egységbezárás csak a tagged rekordtípus (tag=cédula, címke) és a műveletek egy modulba (package) helyezésével valósítható meg.

35 kAkAÓ35 Adatrejtés és interfész Az egységbezárás az alábbi szabályok segítségével valósul meg: Az objektum csak olyan üzenetekre reagál, amelyekre megtanították Az ojektumot csak az interfészen keresztül lehet elérni Az objektum interfésze alehető legkisebb legyen. Módszertani követelmény, hogy az adatok csak a metódusokon keresztül legyenek elérhetők. Ha a nyelvek egy része ad is lehetőséget az adatok közvetlen elérésére, ezt inkább mellőzzük. Az interfész definiálása a programozó feladata. Ezt úgy tudja elvégezni, hogy megadja az objektum osztályában a külvilág számára elérhető metódusokat, esetleg az adatok egy részhalmazát is: ezek nyilvánosak (public). A rejtett (private) adatokat és metódusokat csak az adott objektum használhatja. Egy adat nyilvános volta ideális esetben csak lekérdezési lehetőséget jelent, felülírási, megváltoztatási lehetőséget nem így van például az Eiffelben.

36 kAkAÓ36 Elérhetőség a C++ -ban class A { int i; public: void set_i (int n) {i=n}; int get_i() {return i;}; }; class A1 { private int i; public: void set_i (int n) {i=n}; int get_i() {return i;}; }; class A2 { public: void set_i (int n) {i=n}; int get_i() {return i;}; private int i; }; A C++ -ban az osztályok definíciójában a láthatóság osztályszintű, azaz az azonos osztályba tartozó objektumok el tudják érni közvetlenül egymás rejtett adatait is. Osztályok esetén a private az alapértelmezés, és a nyilvános és rejtett részek tetszés szerinti sorrendben megadhatók. Ezért a baloldali definíciók egyenértékűek.

37 kAkAÓ37 Adatrejtés Object Pascalban Az Object Pascalban private kulcsszóval az objektumok belső használatú részeit jelöljük, de ezek ténylegesen csak az osztályt deklaráló egységen (forrásfájlon) kívül nem láthatóak. Így tehát a módszertani elvárások szerinti adatrejtést csak akkor tudjuk megvalósítani, ha MINDEN OSZTÁLYT KÜLÖN FÁJLBA TESZÜNK. A szokásos módon public jelöli a mindenki által látható részeket. Az alapértelmezés a published láthatóság. A published ugyanolyan láthatóságú, mint a public, a különbség az, hogy futásidejű típusinformáció (RTTI=RunTime Type Information) generálódik minden published típushoz. Így egy applikáció dinamikusan lekérdezheti egy objektummezőit és tulajdonságait, valamint megtalálhatja metódusait. Az RTTI technológia használatos a tulajdonságok kezelésére form fájlok mentésekor és betöltésekor, valamint az eseménykezelő (event handler) metódusok és az események összerendelésekor.

38 kAkAÓ38 Láthatóság Eiffelben Az Eiffel nyelv fejlesztői az ún. szelektív láthatóságot valósították meg. Ez azt jelenti, hogy az osztály minden egyes jellemzőjére (feature) külön megadható, hogy mely osztályok érhessék el. Eiffelben a láthatóság objektum és nem osztály szintű, így ha azt akarjuk, hogy az adott osztály más példányai is elérhessenek egy bizonyos jellemzőt, akkor ezt explicite meg kell adnunk a láthatósági szabályokban. A láthatóságra vonatkozó kívánalmakat úgy adhatjuk meg, hogy a feature kulcsszó után kapcsos zárójelek között felsoroljuk azon osztályoknak a nevét, melyek számára az itt következő jellemzőket láthatóvá kívánjuk tenni. A mindenki számára látható az ANY (ez alapértelmezés, így elhagyható), az adott objektumon kívül seholsem látható a NONE szóval adható meg.

39 kAkAÓ39 Eiffel láthatósági példa class C feature {ANY} x : T; -- minden osztály láthatja feature {C} y: U; -- a C osztály példányai láthatják feature {NONE} z: V; -- csak az adott objektumon belül látható end -- class C

40 kAkAÓ40 Friend metódusok, osztályok C++ -ban A C++ -ban egy másik osztály metódusainak meg lehet engedni, hogy bizonyos esetekben hozzáférjen az objektum adataihoz, és így ismeretségi kapcsolatokat lehet megvalósítani. Ezen metódusokat barát (friend) metódusoknak nevezzük. Ezek nem az osztály objektumainak küldött üzenetek, hanem külső függvények, így nem érvényes az, hogy a Self segítségével látják az objektumot. Ennek megfelelően az aktuális objektumot is meg kell adni egy további paraméterben. Az is megengedett, hogy egy osztály barátnak nyilvánítson egy másik osztályt. Ekkor a barát osztály metódusai elérhetik az illető osztály privát adatait. friend Eljárásnév (par_lista); friend Osztály.Metódus(par_lista); friend Osztálynév;

41 kAkAÓ41 Az osztályok belső állapotát leíró adatokat osztályadatoknak hívjuk. Ezek az adatok az osztályról tárolnak információkat és így nem feltétlenül változnak az egyes objektumok állapotváltozásaival együtt. Helyük az osztály helyfoglalásakor foglalódik le és az osztály helyének felszabadulásakor szabadul fel. Egyes nyelvekben (C++, Java, C#) ezek az osztályváltozók ún. statikus adatok (static) és az osztály objektumai is tudják használni őket. Egy adott osztályváltozóból mindig pontosan egy létezik és az osztály minden egyes példánya ezen „osztozik”. Például egy osztály esetén egy osztályváltozóban számolhatjuk az ehhez az osztályhoz létrehozott objektumokat.

42 kAkAÓ42 class Datum{ int ev, ho, nap; static Datum alapert_datum; // osztálymetódus public: Datum (int ee=0, int hh=0, int nn=0); //… static void beallit_alapert(int,int,int); }; Datum::Datum(int ee, int hh, int nn){ // :: scope access operátor, a globálisra vonatkozik // ha paraméter nélküli konstruktor hívás történik, akkor az alapert_datum értéket kapja // az új objektum ev = ee ? ee : alapert_datum.ev; // ha ee=true vagy nem 0, akkor ee, egyébként … ho = ho ? ho : alapert_datum.ho; nap = nap ? nap : alapert_datum.nap; }; void Datum::beallit_alapert(int e, int h, int n) { // a statikus adattag értékének megváltoztatására Datum::alapert_datum = Datum(e,h,n); }; Datum Datum::beallit_alapert(2003,3,16); // a statikus adattag definiálása

43 kAkAÓ43 Osztálymetódus Object Pascalban Az object Pascalban azt, hogy egy műveletet osztálymetódusként akarunk használni, az osztály definíciójában a class kulcsszóval jelezhetjük. B az osztály objektumainak metódusa, míg ObjSzam az A osztálymetódusa. type A = class procedure B; class function ObjSzam: integer; end; implementation var ObjSz: integer=0; class function A.ObjSzam:integer; begin Result := ObjSz; end; Az ObjSzam osztálymetódus törzsében levő ObjSz rejtett változót az osztály összes objektuma és az osztálymetódus(ok) közösen használhatják.

44 kAkAÓ44 Az osztálydiagramoknál is szokás feltüntetni, ha egy adat osztályadat, vagy ha egy metódus osztálymetódus, ami az adat- vagy metódusnév elé írt C betű jelez. A + jel a nyilvános, a – jel a rejtett elérést jelenti. oszt á ly + nyilv á nos p é ld á nyadat -priv á t p é ld á nyadat +Cnyilv á nos oszt á lyadat -Cpriv á t oszt á lyadat + nyilv á nos p é ld á nymet ó dus -priv á t p é ld á nymet ó dus +Cnyilv á nos oszt á lymet ó dus -Cpriv á t oszt á lymet ó dus

45 kAkAÓ45 Öröklődés Az OO megközelítés a valós világ objektumait – tulajdonságaik alapján – osztályokba sorolja. Minden kategóriának számos alkategóriája lehet, és ezen kategóriák alatt további alkategóriák vannak. Az alosztályok létrehozásakor mindig támaszkodunk annak az osztálynak a tulajdonságaira, amelyiknek az új alosztály része: az alosztály örökli az ősosztály tulajdonságait. Az ősosztály is lehet, hogy valamely más osztály alosztálya, tehát tulajdonságainak egy részét örökölte. A specializációt úgy valósítjuk meg, hogy az egyes alosztályokba új attribútumokat vezetünk be. Például, ha az Emberek osztályt az Élőlények osztályból származtatjuk, akkor bevezethetjük például az anyanyelv attribútumot, amely csak az Emberek alosztály tagjaira érvényes.

46 kAkAÓ46 Lehetőséget kell biztosítani arra is, hogy az egyes alosztályokban bővítsük a műveletek halmazát olyan műveletekkel, amelyek az ősben nincsenek. Az altípusképzés során megengedjük a változtatást is, azaz hogy az új osztályban egyes műveletek átdefiniálhatók legyenek, implementációjuk, esetleg specifikációjuk is különbözzön az ősosztálybeliétől. Dinamikus összekapcsolás kell, hogy lehetőséget adjon arra, hogy a változó aktuális típusa – futásközben – határozza meg a művelet végrehajtandó implementációját. A műveletek átdefiniálása nem történhet tetszőlegesen (erre még visszatérünk). Ezt a műveletet specifikáció öröklésnek nevezzük. Egy meglevő osztályt bármikor felhasználhatunk más osztályok definiálására azzal a céllal is, hogy a már meglévő kódot újra fel tudjuk használni. Ez akkor valósul meg, ha a leszármaztatott osztály implementációjában rejtetten használjuk az ősosztályt. Ezt nevezhetjük újrafelhasználó öröklésnek vagy implementációs öröklődésnek.

47 kAkAÓ47 Az öröklődés lehet egyszeres vagy többszörös. Egyszeres öröklődésről akkor beszélünk, ha a leszármaztatott osztálynak pontosan egy közvetlen ősosztálya van. Többszörös öröklődés esetén a leszármaztatottnak legalább két közvetlen ősosztálya van. A leszármaztatott osztály mindig örökli az ősosztály adatait és metódusait. A többszörös öröklődés esete gondot okozhat, ha például két közvetlen ősosztályban is szerepel egy-egy ugyanolyan nevű adat vagy metódus, akkor a leszármaztatott osztályban ez többértelműséghez vezethet (erre visszatérünk).

48 kAkAÓ48 C++ példa öröklődésre class Sikidom{ protected: // származtatott osztályokban látható int szelesseg, magassag; public: void beallit_ertekek (int a, int b){ Szelesseg=a; magassag=b;} }; class Teglalap: public Sikidom{ public: int terulet(){ return (szelesseg*magassag);} }; class Haromszog: public Sikidom{ public: int terulet(){ return (szelesseg*magassag/2);} }; A Teglalap és a Haromszog osztály is rendelkezik a Sikidom osztályban bevezetett adattagokkal és metódussal. A terulet() metódus új mindkét osztályban, más-más módon definiálva.

49 kAkAÓ49 class Egyenlooldalu{ public: bool e; }; class Negyzet: public Teglalap, public Egyenlooldalu{ Negyzet() { e = true;} }; A C++ megengedi a többszörös öröklődést is, ha például az előzőeken túl bevezetjük az Egyenlooldalu osztályt is, akkor a Negyzet osztály lehet ennek és a Teglalap osztálynak is a leszármazottja.

50 kAkAÓ50 Öröklődés az Object Pascalban type Tallat = class public function eszik: string; private fajta: string; end; TKutya = class(TAllat) public function ugat: string; end; var Kutya1 : TKutya; begin {….} writeln(Kutya1.eszik); {….} writeln(Kutya1.ugat); end. Az Object Pascal az egyszeres öröklődést támogatja, interfészek segítik a többszörös öröklődést (ld. később). A TAllat leszármazottja a TKutya, itt vezetjük be a kutyákra jellemző ugat függvényt.

51 kAkAÓ51 Öröklődés az Eiffelben Eiffelben az öröklődési kapcsolatok az osztály definíciójában az inherit kulcsszó után adhatók meg. A leszármazott az ősének összes adattagját és metódusát elérheti, és adott esetben módosíthatja az öröklődés során. Az öröklődési záradékban az inherit kulcsszó után fel kell sorolni azoknak az ősosztályoknak a nevét, amelyektől örökölni szeretnénk,és az egyes ősök redefine záradékaiban kell megadjuk azokat a jellemzőket, amelyeket módosítani szeretnénk az öröklődés során. Azok a jellemzők, amelyeket nem említünk meg a redefine záradékban, természetesen változatlanul öröklődnek tovább. A nyelv a többszörös öröklődést lehetővé teszi (ld. később).

52 kAkAÓ52 class SOKSZOG inherit ALAKZAT feature kiir is do io.putstring(”SOKSZÖG VAGYOK”) end; end class NEGYZET inherit SOKSZOG redefine kir end feature kiir is do io.putstring(”NÉGYZET VAGYOK”) end; end SOKSZOG őse alakzat, NEGYZET őse SOKSZOG, kiir-t újradefiniáljuk a NEGYZET-ben.

53 kAkAÓ53 Adatrejtés és öröklődés kapcsolata külvilágaz objektumleszármazott publicelérhető protectednem elérhetőelérhető privatenem elérhetőelérhetőközvetlenül nem elérhető Object Pascalban a privát rejtésre vonatkozó megállapítás csak a különböző fordítási egységben bevezetett osztályok esetén igaz.

54 kAkAÓ54 Tegyük fel, hogy az A osztály tartalmaz egy nyilvános m metódust és egy privát i adatot. Feltételezzük, hogy az m metódus használja az i adatot. Ha a B osztály az A-ból öröklődik, akkor természetesen örökli az m metódust, és ez minden további nélkül használni tudja az i adatot, habár az nem látható B-ben, és így egyetlen más új B-beli metódus sem tudja közvetlenül használni. A rejtettként deklarált adatok és metódusok is átöröklődnek, csak ezek nem lesznek láthatók a leszármazott osztályban. A - i + m B Számos programozási nyelv megengedi azt is hogy egyszerű újradeklarálással a leszármaztatott osztály megváltoztassa az adatok, metódusok adatrejtési módját. Például, ha a leszármaztatottban nyilvánosként vezettünk be egy, az ősben már eleve védettként bevezetett metódust, akkor ez a művelet a továbbiakban nyilvános lesz.

55 kAkAÓ55 Tegyük fel, hogy a Kör osztály az Alakzat osztály örököseként jött létre. Ekkor a Kör objektumaira igaz, hogy azok Alakzat típusúak is. De ha egy Kör típusúként deklarált kiskör objektumot tekinthetünk Alakzat típusúként is, akkor értékül is adhatjuk egy alak nevű, Alakzat típusú objektumnak, azaz megengedett az alak:=kiskör értékadás. Ugyanakkor, mivel fordítva nem igaz az állítás, hiszen egy Alakzat-beli objektum nem feltétlenül esik a Kör által leírt altípusba, ezért a kiskör:= alak értékadás nem megengedett. Hogyan oldjuk fel, hogy az alak:=kiskör értékadás ne okozzon típuskeveredést?

56 kAkAÓ56 Vezessük be a statikus és dinamikus típus fogalmát. Egy változó statikus típusa az az osztály, mellyel a deklaráció során összekapcsoltuk. A változó dinamikus típusa pedig a statikus típus vagy annak egy leszármazottja lehet. Az előző példában az alak statikus típusa az Alakzat osztály, míg az alak:=kiskör értékadás után dinamikus típusa a Kör osztály. Azt a lehetőséget, hogy egy változó a program futása során több osztály objektumára is hivatkozhat, nevezzük a változók polimorfizmusának (többalakúságának). Az öröklődés során megengedjük, hogy a leszármazottaknál az egyes műveletek implementációja és/vagy specifikációja különbözzön az ősosztályban bevezetett művelettől. Például, ha az Alakzat-nak van egy Kirajzol művelete,és ezt a Kör osztályban felülbíráltuk, akkor az alak:=kiskör értékadás után azt várjuk, hogy az alak-ra alkalmazott Kirajzol művelet a Kör osztályban meghatározott műveletet végezze el. A dinamikus összekapcsolás – amely futásidejű esemény – ad lehetőséget arra, hogy a változó dinamikus típusa határozza meg a végrehajtandó implementációját. Ezt virtuális műveletnek nevezik.

57 kAkAÓ57 Statikus és dinamikus átdefiniálás Több programozási nyelvben (például Object Pascal vagy C++) az objektumok metódusai alapértelmezésben statikus kötésűek. Például az alak.Kirajzol esetén az alak statikus típusa alapján az Alakzat osztályban, míg a kiskör statikus típusa alapján a Kör osztályban definiált Kirajzol művelet meghívása szerepel a végrehajtandó kódban. Dinamikus kötés esetén a meghívott metódus címe ténylegesen csak futási időben derül ki, hiszen ha valamely osztály változójának a leszármazott osztályok objektumai is értékül adhatók, akkor csak futás közben lehet eldönteni, hogy az adott metódus melyik osztálybeli megvalósítását kell meghívni. Kifejezetten objektumorientáltnak tervezett nyelveknél (Smalltalk, Java, Eiffel) ez utóbbi az alapértelmezés. Egyes nyelveknél lehet virtuális metódusokat deklarálni, és a virtuális metódusok hívásai lesznek dinamikus kötésűek. Egy új osztály deklarálásakor virtuális metódusai bekerülnek az osztály ún. Virtuális Metódus Táblájába (VMT). Virtuális metódus hívása esetén a program a megfelelő VMT-ből olvassa ki, hogy melyik metódust kell meghívnia.

58 kAkAÓ58 #include class Teglalap{ protected:int szelesseg, magassag; public:Teglalap(int a, int b){szelesseg=a; magassag=b;}; virtual int f(){ return (0); } }; class Szines_Teglalap: public Teglalap{ int szin; public:Szines_Teglalap(int a, int b, int c): Teglalap(a,b), szin(c) {}; virtual int f() { return(szin); } }; void main(){ clrscr(); Teglalap a(2,3); Szines_Teglalap t(3,4,5); a = t; cout << a.f()<<"..."; // csonkítás Teglalap* a1 = &a; Teglalap* a2 = &t; cout f() f(); } 0…0---5 lesz az output. Itt a poliformizmus látszólagos, mert az f() függvényt statikusan köti az objektumhoz. Itt valódi a poliformizmus és megvalósul a dinamikus kötés. C++

59 kAkAÓ59 Az Object Pascal változói objektumok referenciái, a polimorfizmus azt jelenti, hogy az ősosztálybeli referencia aktuálisan valamelyik leszármazott osztály objektumára is hivatkozhat. A metódusok alapértelmezésben statikusak. Dinamikus metódust a virtual kulcsszóval kell bevezetni: procedure f; virtual Ha ezt a leszármazottban szeretnénk felüldefiniálni, akkor az override kulcsszóval tehetjük meg: procedure f; override Lehetséges a metódusnevek túlterhelése is, ehhez az overload kulcsszót használjuk: procedure f(a: string); overload Túlterhelésnél – azonos paraméterezés esetén – az újonnan bevezetett metódus eltakarja a régit.

60 kAkAÓ60 Az ősosztály metódusainak meghívása Szükség lehet arra, hogy meghívjuk az ősosztály vagy az ősosztályok metódusait, vagy akár arra is, hogy a gyermekosztályban egy örökölt metódust úgy bővítsünk ki, hogy felhasználjuk a már meglevő, az ősosztályban megírt metódust. Object Pascalban az inherited kulcsszóval hivatkozhatunk egy metóduson, vagy akár egy konstruktoron belül is az ősosztály ugyanilyen nevű metódusára. type TSzulo = class… procedure f(…); virtual;… end; type TGyerek = class (TSzulo) procedure f(…); override;… end; procedure TGyerek.f(…); begin inherited f(…);… end; Delphi Itt a TSzulo f műveletét hívjuk!

61 kAkAÓ61 Többszörös öröklődés Többszörös öröklődésről beszélünk, ha egy osztálynak egynél több közvetlen őse van. Ez azt jelenti, hogy az adott osztály két (vagy több) osztály alosztályának is tekinthető. Például a VALLALATI_REPULOGEP osztály mind a REPULOGEP, mind a VALLALAT osztály alosztályaként kezelhető.

62 kAkAÓ62 #include class A{ public:virtual void f(){ cout << "f az A-ban\n"; } }; class B{ public:virtual void g() = 0; }; class C : public A, public B { public: void f() {cout << "f a C-ben\n";}; void g() {cout << "g a C-ben\n";}; }; int main(){ clrscr(); A* a1; B* b1; C c; a1 = &c; b1 = &c; a1->f(); b1->g(); return 0; // cout f()<<"---"; // cout f(); } C++ Output: f a C-ben g a C-ben f specifikációsan öröklődik A-tól, g implementációsan öröklődik B-től Az ősökön keresztül csak a tőlük örökölt metódusok érhetők el, a1->g(); értelmetlen. C++

63 kAkAÓ63 #include class A{ public:virtual void f(){ cout << "f az A-ban\n"; } }; class B{ public:virtual void f(){cout<<"f a B-ben\n";}; }; class C : public A, public B { public: virtual void f() {cout << "f a C-ben\n";}; }; int main(){ clrscr(); A* a1; B* b1; C c; a1 = &c; b1 = &c; a1->f(); b1->f(); c.f(); return 0; } Output: f a C-ben C++

64 kAkAÓ64 A rombusz-öröklődés problémája A többszörös öröklődés egy különleges lehetősége az ismételt öröklődés, amikor egy leszármazott valamelyik ősosztályát az öröklődési gráfban többféle úton is eléri, többször örökli ugyanazokat az attribútumokat vagy metódusokat. Ha ugyanis egy A ősosztályban létezik legalább egy adat (attr_a) vagy egy metódus (f), az A osztály minden leszármazottja örökli ezeket. Tegyük fel, hogy az A osztálynak van két leszármazottja, B és C, ekkor ezeknek is lesz egy attr_a attribútuma és f metódusa, sőt át is definiálhatják az öröklött f metódust. Ha létrehozunk egy új D leszármazottat, amely többszörös öröklődést használva B-ből és C-ből öröklődik, akkor hány példányban jelenjen meg D-ben az örökölt adat vagy metódus? A válasz nyelvi megoldásonként különbözik.

65 kAkAÓ65 C++ -ban az az alapértelmezés, hogy a leszármazottban minden adattag annyiszor jelenik meg, ahányszor a közös őstől örököltük, s az osztálynévvel minősítve különböztetjük meg az azonos nevű adattagokat. Lehetnek olyan esetek, amikor nincs értelme a közös őstől örökölt tagokat többszörözni. Például, ha a Szemely osztályt jellemezzük a Neve, Szuletesi_hely adatokkal, és származtatjuk többszörös öröklődéssel a Hallgatoi_gyakvez osztályt, aki Tanar és Diak egyszerre, akkor nincs értelme, hogy a Neve, Szuletesi_hely adatokat egynél többször tároljuk. A Tanar és a Diak tekinthetik ősüket virtuálisnak, és ekkor a Szemely osztálytól örökölt adatok csak egyszer jelennek meg a Hallgatoi_gyakvez osztályban. Ha a Szemelyben egyszer és többször tárolandó adattagok is vannak, a vegyes kezelés nem oldható meg. Eiffelben igen. Az alapértelmezés szerint – ellentétben a C++-szal – a közös őstől származott jellemzők csak egyszer jelennek meg, a Hallgatoi_gyakvez viszont átnevezheti valamelyiket és ezáltal többszörözheti.

66 kAkAÓ66 Javasolt ellenőrző kérdések 1.Milyen szabályok és ajánlások fogalmazhatók meg az egységbezárásnál? 2.Mire való a self és a this? 3.Milyen probléma megoldásakor érdemes friend osztályt használni? 4.Mely mindennapi életből vett feladattal szemléltetné az öröklődést? 5.Ismertesse példán keresztül a dinamikus kötést! 6.Ismertesse a polimorfizmus lehetőségeit C++-ban! 7.Ismertesse a különböző tanult nyelvekben a konstruktor és destruktor lehetőségeket! 8.Mi a rombusz öröklődés problémája? 9.Ismertesse a C++ többszörös öröklődésre vonatkozó megoldását példán keresztül! 10.Jellemezze a C++ öröklési módjait. 11.Jellemezze az Object Pascal adatrejtését. 12.Sorolja fel a programnyelvekkel szemben támasztott 7 elvárást. 13.Mire való C++-ban a make? 14.Mit jelent az azonosító túlterhelése illetve felüldefiniálása? 15.Mi az előnye a típusspecifikáció és a megvalósítás szétválasztásának? 16.Adjon példát specifikációs és implementációs öröklődésre!


Letölteni ppt "Absztrakt adattípusok kAkAÓ 2004. Objektumorientált programozás. In: Nyékyné Gaizler Judit (szerk.): Programozási nyelvek, Kiskapu, Budapest, 2003. pp.333-350."

Hasonló előadás


Google Hirdetések