OOP alapok Egy OOP nyelvet három fontos dolog jellemez. egységbezárás ( encapsulation objektumoknak öröklés ( inheritance



Hasonló dokumentumok
AZ OOP KÓDOLÁSI ALAPJAI

Visual C++ osztály készítése, adattagok, és metódusok, láthatóság, konstruktor, destruktor. Objektum létrehozása, használata, öröklés.

Számítástechnika II. BMEKOKAA Előadás. Dr. Bécsi Tamás

Java programozási nyelv 4. rész Osztályok II.

Statikus adattagok. Statikus adattag inicializálása. Speciális adattagok és tagfüggvények. Általános Informatikai Tanszék

Programozás II. 3. gyakorlat Objektum Orientáltság C++-ban

JAVA PROGRAMOZÁS 2.ELŐADÁS

C++ programozási nyelv Konstruktorok-destruktorok

Virtuális függvények (late binding)

Osztályok. 4. gyakorlat

C++ programozási nyelv Struktúrák a C++ nyelvben

Programozás II. 2. gyakorlat Áttérés C-ről C++-ra

Számítástechnika II. BMEKOKAA Előadás. Dr. Bécsi Tamás

Java programozási nyelv 5. rész Osztályok III.

Interfészek. PPT 2007/2008 tavasz.

Pelda öröklődésre: import java.io.*; import java.text.*; import java.util.*; import extra.*;

Osztály és objektum fogalma

Miután létrehoztuk, szeretnénk neki beszédesebb nevet adni. A név változtatásához a következőt kell tenni:

Programozás II. 4. Dr. Iványi Péter

C++ programozási nyelv

Objektumorientált programozás C# nyelven

Programozás C++ -ban

OOP #1 (Bevezetés) v :39:00. Eszterházy Károly Főiskola Információtechnológia tsz. Hernyák Zoltán adj.

Programozás BMEKOKAA146. Dr. Bécsi Tamás 7. előadás

Objektumorientált szoftverfejlesztés alapjai

Objektumok inicializálása

Java VI. Miskolci Egyetem Általános Informatikai Tanszék. Utolsó módosítás: Ficsor Lajos. Java VI.: Öröklődés JAVA6 / 1

Már megismert fogalmak áttekintése

OOP #14 (referencia-elv)

Programozás II. 2. Dr. Iványi Péter

Java és web programozás

Dr. Pál László, Sapientia EMTE, Csíkszereda WEB PROGRAMOZÁS 2.ELŐADÁS. Objektumorientált programozás

Kivételkezelés, beágyazott osztályok. Nyolcadik gyakorlat

Származtatási mechanizmus a C++ nyelvben

Bevezetés, a C++ osztályok. Pere László

Java III. I I. Osztálydefiníció (Bevezetés)

Öröklés és Polimorfizmus

Bevezetés a programozásba Előadás: Tagfüggvények, osztály, objektum

III. OOP (objektumok, osztályok)

Java III. I I. Osztálydefiníció (Bevezetés)

Java és web programozás

A C programozási nyelv IV. Deklaráció és definíció

OOP. Alapelvek Elek Tibor

1. Mi a fejállományok szerepe C és C++ nyelvben és hogyan használjuk őket? 2. Milyen alapvető változókat használhatunk a C és C++ nyelvben?

Objektumelvű alkalmazások fejlesztése 6. gyakorlat. Öröklődés, polimorfizmus. Öröklődés Kódismétlődés objektum-orientált szerkezetben

és az instanceof operátor

Bevezetés a Python programozási nyelvbe

Java VIII. Az interfacei. és az instanceof operátor. Az interfészről általában. Interfészek JAVA-ban. Krizsán Zoltán

BME MOGI Gépészeti informatika 8.

Programozás módszertan p.1/46

Programozás. (GKxB_INTM021) Dr. Hatwágner F. Miklós március 3. Széchenyi István Egyetem, Gy r

Objektumorientált programozás C# nyelven

Bevezetés a C++ programozási nyelvbe

Programozás C++ -ban 2007/7

C# osztálydeníció. Krizsán Zoltán 1. .net C# technológiák tananyag objektum orientált programozás tananyag

C++ programozási nyelv

Web-technológia PHP-vel

Pénzügyi algoritmusok

Java és web programozás

C++ programozási nyelv

A JavaScript főbb tulajdonságai

Programozás C++ -ban

Széchenyi István Egyetem. Programozás III. Varjasi Norbert

Interfészek. Programozás II. előadás. Szénási Sándor.

OOP: Java 8.Gy: Abstract osztályok, interfészek

Programozás C++ -ban 2007/4

Programozás II. 6.Öröklés Dr. Iványi Péter

Java VI. Egy kis kitérő: az UML. Osztály diagram. Általános Informatikai Tanszék Utolsó módosítás:

Programozás C és C++ -ban

Programozási technikák Pál László. Sapientia EMTE, Csíkszereda, 2009/2010

Globális operátor overloading

A C programozási nyelv V. Struktúra Dinamikus memóriakezelés

Programozási alapismeretek 4.

3. Osztályok II. Programozás II

A verem (stack) A verem egy olyan struktúra, aminek a tetejéről kivehetünk egy (vagy sorban több) elemet. A verem felhasználása

C programozási nyelv Pointerek, tömbök, pointer aritmetika

1. Bevezetés A C++ nem objektumorientált újdonságai 3

A C++ szigorúbban kezeli a típuseltéréseket, mint a C nyelv Lehetséges típuskonverziók:

Programozás alapjai. (GKxB_INTM023) Dr. Hatwágner F. Miklós október 11. Széchenyi István Egyetem, Gy r

Programozási nyelvek Java

Programozási nyelvek Java

Programozás I. 5. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar

Osztályok. construct () destruct() $b=new Book(); $b=null; unset ($b); book.php: <?php class Book { private $isbn; public $title;

OOP. #6 (VMT és DMT) v :33:00. Eszterházy Károly Főiskola Információtechnológia tsz. Hernyák Zoltán adj.

Objektum orientált programozás (Object Oriented Programming = OOP)

Bevezetés a programozásba Előadás: Objektumszintű és osztályszintű elemek, hibakezelés

Java V. Osztályszint. lyszintű ű tagok. Példányváltozó. Osztályváltozó. Általános Informatikai Tanszék Utolsó módosítás:

Programozás C++ -ban

Programozási nyelvek Java

Programozás II gyakorlat. 6. Polimorfizmus

Programozás BMEKOKAA146. Dr. Bécsi Tamás 5. előadás

GENERIKUS PROGRAMOZÁS Osztálysablonok, Általános felépítésű függvények, Függvénynevek túlterhelése és. Függvénysablonok

Helyes-e az alábbi kódrészlet? int i = 1; i = i * 3 + 1; int j; j = i + 1; Nem. Igen. Hányféleképpen lehet Javaban megjegyzést írni?

Programozás. C++ osztályok. Fodor Attila. Pannon Egyetem Műszaki Informatikai Kar Villamosmérnöki és Információs Rendszerek Tanszék

0.2.1 Operátorok túlterhelése (műveletek definiálhatók felhaszn. típusokra) Kutya. Eb1. Eb2. Név (txt): Rex. Blöki. Német juhász 3

Java Programozás 3. Ea: Java osztályok. OOP alapalapok

Adatstruktúrák, algoritmusok, objektumok

8. gyakorlat Pointerek, dinamikus memóriakezelés

1. Egyszerű (primitív) típusok. 2. Referencia típusok

500. AA Megoldó Alfréd AA 500.

Átírás:

Az objektum-orientált programozás (röviden OOP) a természetes gondolkodást, cselekvést közelítő programozási mód, amely a programozási nyelvek tervezésének természetes fejlődése következtében alakult ki. Egy OOP nyelvet három fontos dolog jellemez. Az egységbezárás (encapsulation) azt takarja, hogy az adatstruktúrákat és az adott struktúrájú adatokat kezelő függvényeket kombináljuk; azokat egy egységként kezeljük, és elzárjuk őket a külvilág elől. Az így kapott egységeket objektumoknak nevezzük. Az objektumoknak megfelelő tárolási egységek típusát a C++-ban osztálynak (class) nevezzük. Az öröklés (inheritance) azt jelenti, hogy adott, meglévő osztályokból levezetett újabb osztályok öröklik a definiálásukhoz használt alaposztályok már létező adatstruktúráit és függvényeit. Ugyanakkor újabb tulajdonságokat is definiálhatnak, vagy régieket újraértelmezhetnek. Így egy osztályhierarchiához jutunk.

A többrétűség (polymorphism) alatt azt értjük, hogy egy adott tevékenység (metódus) azonosítója közös lehet egy adott osztályhierarchián belül, ugyanakkor a hierarchia minden egyes osztályában a tevékenységet végrehajtó függvény megvalósítása az adott osztályra nézve specifikus lehet. Az ún. virtuális függvények lehetővé teszik, hogy egy adott metódus konkrét végrehajtási módja csak a program futása során derüljön ki. Ugyancsak a többrétűség fogalomkörébe tartozik az ún. overloading, aminek egy sajátságos esete a C nyelv standard operátorainak átdefiniálása (operator overloading). A C++ egyik fontos tulajdonsága, hogy lehetőségünk van az adataink és az őket manipuláló programkód összeforrasztására, egy egységbe zárására, egy osztályba foglalására. (Ez az ún. encapsulation.) Például tegyük fel, hogy létrehozunk egy verem adatszerkezetet. (most az egyszerűség kedvéért valósítsuk meg tömbbel).

A hagyományos C-ben az a szokásos megoldás, hogy az adatstruktúráinkat és a hozzájuk tartozó függvényeket egy önálló, külön fordítható forrásmodulban helyezzük el. (akarmi.h) Ez a megoldás már elég elegáns, de az adatok és az őket manipuláló függvények között még nincs explicit összerendeltség, továbbá más programozók egy másik modulból direkt módon is hozzáférhetnek az adatainkhoz, anélkül, hogy az adatok kezelésére szolgáló függvényeinket használnák. Ilyen esetekben az alap-adatstruktúra megváltozása fatális hibát okozhat egy nagyobb project-ben. A példánkban maradva van egy VEREM nevű tömb VMUT nevű változó, egy BETESZ, egy KIVESZ függvény, esetleg egy URES_E függvény. A programozó azonban közvetlenül is manipulálhatja a VEREM tömböt. Adhat neki értéket, stb. Pl. lehet a programban olyan, hogy VEREM[i]=8; Ha valaki közben dinamikus veremre tér át, akkor nincs is VEREM tömb. Minden közvetlen hivatkozás hibaüzenetet eredményez.

A C++-ban speciális tárolási egységfajták, az osztályok szolgálnak arra, hogy adatokat és a hozzájuk rendelt függvényeket egy egységként, egy objektumként kezelhessünk. Az osztályok alaptípusa a C++-ban a class. A C++-ban egy osztály típusú tárolási egység (class) függvényeket (ún. függvénymezőket - angolul member functions) kombinál adatokkal (adatmezőkkel - angolul data members), és az így létrejött kombinációt elrejtjük, elzárjuk a külvilág elől. Ezt értjük az egységbezárás alatt. Egy class-deklaráció hasonló a jól ismert struktúradeklarációhoz: class osztálynév { }; Pl.: class verem {... }; Most már használható mint akármelyik típus. Deklarálhatsz ilyen változót (objektumot). verem v1, v2

Öröklés Az objektumok örökölhetnek tulajdonságokat a szülő objektumtól. Nézzünk egy példát. Létre akarunk hozni valami iskolai programot. A képernyőn rendezett kiírást szeretnénk (a Név mindig az 5. pozíción van, a Születési hely a 35.-en, stb. Létrehozunk egy személy osztályt amiben a pozíció és az odamenő függvény van. class szemely { string nev, szulhely, stb; void megjelenit() } Vannak tanárok és tanulók, ezekkel az adatokkal mindenki rendelkezik, de még további tulajdonságokkal is. A tanároknak van szakjuk, stb, a tanulóknak vannak tantárgyaik, gondviselőjük, stb

Akkor most létrehozhatunk olyan objektumokat, amelyek öröklik a meglevő tulajdonságokat: class tanar : szemely { itt jöhetnek a további jellemzők }; class tanulo : szemely { itt a tanulo további jellemzői }; Az öröklés alakja: class osztálynév : szülő osztály neve1, szülő osztály neve2 { }; A többszörös öröklés során a gyermek osztály örökli az összes szülő minden tulajdonságát. Vigyázat!!! A többszörös öröklést nem támogatja minden c++ fordító, a többszörös öröklés csak az AT&T 2.0-ás verziójú C++ fordítójával kompatibilis C++ rendszerekben lehetséges.

A többrétűség (vagy sokalakúság, sokoldalúság) a C++-ban azt jelenti, hogy egy adott őstípusból származtatott további típusok természetesen öröklik az őstípus minden mezőjét, így a függvénymezőket is. De az evolúció során a tulajdonságok egyre módosulnak, azaz például egy öröklött függvénymező neve ugyan nem változik egy leszármazottban, de esetleg már egy kicsit (vagy éppen nagyon) másképp viselkedik. Példánkban a szemely osztály tartalmaz megjelenítés függvényt, ami kiírja az adatokat. A tanar osztály örökli a megjelenítés függvényt, de most nem ott és nem azokat az adatokat jeleníti meg. Ezt a C++-ban a legflexibilisebb módon az ún. virtuális függvények (virtual functions) teszik lehetővé. A virtuális függvények biztosítják, hogy egy adott osztály-hierarchiában (származási fán) egy adott függvény különböző verziói létezhessenek úgy, hogy csak a kész program futása során derül, hogy ezek közül éppen melyiket kell végrehajtásra meghívni.

Ezt a mechanizmust, azaz a hívó és a hívott függvény futási idő alatt történő összerendelését késői összerendelésnek (late binding) nevezzük. Tanultuk már, hogy ugyan azzal a névvel lehet függvényeket létrehozni. A paraméterlistából dönti el a fordító, hogy melyik változatot hívja meg. Ez még fordítási időben történik. Ennek továbbfejlesztése a futási időben történő összerendelés. Az előzőekben láthattuk, hogy egy osztálynak nemcsak adatmezői, hanem függvénymezői (function members) is lehetnek. Hogyan hozhatunk létre függvénymezőket?

vagy az osztály-definíción belül definiáljuk a függvényt (ún. implicit inline függvényként), vagy az osztálydefiníción belül csak deklaráljuk, és azután valahol később definiáljuk. A két módszer különböző szintaxissal rendelkezik. Lássunk egy példát az első lehetőségre: class verem { int vmut, verem[100]; void betesz (int mit) {verem[vmut]=mit; vmut ++;}; }; A másik lehetőség a függvény prototípussal egyezik meg. class verem { int vmut, verem[100]; void betesz (int mit); }; void verem::betesz(int mit) {verem[vmut]=mit; vmut ++;};

Figyeljük meg, hogyan használtuk a :: hatáskört definiáló operátort (scope resolution operator). A verem osztályazonosító szükséges ahhoz, hogy a fordítóprogram tudja, melyik osztályhoz is tartozik a betesz függvénydefiníciója, valamint tudja azt is, hogy mi az érvényességi tartománya. Ez tehát azt jelenti, hogy a verem::betesz függvény (a hagyományos globális változókon kívül) csak a verem osztályhoz tartozó objektumok mezőihez férhet hozzá. Persze létezhet több, más osztályhoz tartozó betesz függvény is. Az osztály-deklaráción belüli függvény-definíció esetében természetesen nem volt szükség az érvényességi tartományt definiáló verem:: előtagra, hiszen a betesz függvény hovatartozása abban az esetben teljesen egyértelmű volt. A hovatartozás definiálásán túlmenően a verem::-nak más szerepe is van. Hatása kiterjed az őt követő függvénydefinícióra oly módon, hogy a betesz függvényen belül a vmut azonosítójú változóra való hivatkozás a verem osztály vmut azonosítójú adatmezőjére vonatkozik, valamint a betesz függvény a verem osztály hatáskörébe kerül.

Elképzelhető, hogy egy származtatott típusban lokálisan deklarálunk egy függvénymezőt, melynek azonosítója és paraméterlistája is megegyezik egy őstípusban definiált függvénymező azonosítójával és paraméterlistájával, ugyanakkor a definíció során fel szeretnénk használni az őstípusbeli változatot. Mivel a későbbi definícióban lévő függvénymező a saját hatáskörében elfedi a korábbi definíciót, a származtatott típusban csak az érvényességi tartományt definiáló operátor segítségével használhatjuk az őstípusban definiált, "elfedett" függvénymezőt. class szemely { string nev, szulido; void megjelen(int sor);}; class tanar : szemely { string dszam, szak1,szak2; void megjelen(int sor);}; void tanar::megjelen(void) { szemely::megjelen(sor); gotoxy(40,sor) cout <<dszam; gotoxy(50,sor); cout<<szak1; } Ha van egy globális megjelen függvény is, akkor arra a továbbiakban ::megjelen(5) alakban hivatkozhatunk!!!!

A függvénymezők egy adott típusú adathalmazon végrehajtandó műveleteket jelentenek. Amikor tehát a betesz vagy megjelen függvényt meghívjuk, tudatnunk kell vele azt is, hogy most éppen melyik definiált verem, tanar, szemely típusú objektum-példány (object instance) függvényére van szükségünk. A megoldás a hagyományos C struktúra-mezőkhöz való hozzáférés szintaktikájának kiterjesztése. Az általános szintaxis a következő: objektumnév.függvénymező-név( argumentumlista) Pl. tanar tan1, *tanptr, tanarok[100]; tan1.megjelenit(5); tanarok[23].megjelenit(1); DE!!! tanptr->megjelen(3);

A this nevű, implicit mutató Egy függvénymezőben direkt módon is hivatkozhatunk arra az objektumra, amelyiknek a függvénymezejét aktivizáltuk. Például a class valami { int m; public: int read_m(void) { return m; } }; valami egyik, masik; deklaráció esetén, az egyik.read_m( ), illetve a masik.read_m( ) függvényhívások esetén az egyik.m, illetve masik.m értéket adja vissza. A függvényhívás során úgy derül ki, hogy melyik objektum adatmezőit kell használni, hogy minden függvénymező számára implicit módon deklarálásra kerül egy this nevű pointer. Ha tehát a read_m függvény egy valami típusú osztály függvénymezője, akkor a read_m-en belül this egy valami* típusú pointer, ilyen módon az első read_m hivatkozásnál this az egyik változóra, míg a második hivatkozás alkalmával a masik-ra mutat. A this mutató explicit módon is megjelenhet a függvénymezők definíciója során: class valami {int m; public: int read_m(void) { return this->m; } };

Konstruktorok és destruktorok Két speciális, előredefiniált függvénymező-fajta létezik, amelyek kulcsszerepet játszanak a C++ban. Ezek a konstruktorok ( constructors) és a destruktorok (destructors). Általános probléma a hagyományos nyelveknél az inicializálás. Mielőtt használnánk egy adatstruktúrát, gondoskodnunk kell arról, hogy megfelelő tárterületet biztosítsunk az adatstruktúra számára és megfelelő kezdeti értékkel rendelkezzen az adott típusú változó. Pl.: a verem osztálynál a vmut értékét kezdetben nullára kell állítani. A megoldás a konstruktor használata. A konstruktor mindig lefut, ha egy osztálypéldányt létrehozunk. Akkor is ha nem írtuk meg!!! Ekkor egy alapértelmezett konstruktor hajtódik végre.

class verem {int vmut, verem[100]; void betesz(int mit); verem(void) {vmut=0}; }; A konstruktort ugyanúgy deklarálhatjuk, mint minden egyéb függvénymezőt. A konstruktor neve és az osztály neve ugyanaz, mind a kettő verem. Innen tudja a fordítóprogram, hogy az adott függvénymező a konstruktor. Figyeljük meg azt is, hogy mint minden függvénynek, a konstruktornak is lehetnek paraméterei (bár most éppen nincs), a konstruktor törzse pedig egy szabályos függvénytörzs. Az egyetlen, de lényeges különbség, hogy egy konstruktornak nem lehet típusa, még void sem! (Így értéket sem adhat vissza.) Az overloading a konstruktorok esetében is alkalmazható, így egy osztálynak több konstruktora is lehet, és mindig az aktuális paraméterlista alapján dől el, hogy melyik változatot kell aktivizálni. Ha nem definiálunk mi magunk egy konstruktort, akkor a C++ generál egyet, amelynek nincsenek argumentumai.

Lehetséges azonban a következő: class szemely {string nev, szulhely; szemely(string benev="", string beszulhely="") { nev=benev; szulhely=beszulhely;} } Ekkor a szemely ember("kis Péter"); deklaráció hatására létrejön az ember változó, ami személy osztályú és az ember.nev Kis Péter lesz, az ember.szulhely pedig üres. Általában egy C++ trükk, hogy az általunk definiált konstruktor argumentumaihoz egy alapértelmezést rendelhetünk, mint ahogy azt a szemely::szemely függvény esetében is tettük. Egy speciális konstruktor az ún. másoló konstruktor ( copy constructor). Ha s egy osztály, akkor a hozzá tartozó másoló konstruktor alakja s::s(s&).

Destruktorok definiálása Ahogy konstruktorokat definiálhatunk, ugyanúgy definálhatunk saját magunk destruktorokat is. A statikus objektumok számára a C++ futtató rendszer a main meghívása előtt foglal tárterületet és a main befejezése után felszabadítja azt. Az auto objektumok esetében a tárterület felszabadítás akkor történik meg, amikor az adott változó érvényét veszti (tehát az objektum definíciót tartalmazó blokk végén). Az általunk definiált destruktorokat akkor aktivizálja a C++ futtató rendszer, amikor egy objektumot meg kell szüntetni. class verem {int vmut, verem[100]; void betesz(int mit); verem(void) {vmut=0}; ~verem(void) {cout<<"megszunt A VEREM";}; };

Mező hozzáférés hozzáférési mód : mezőlista ahol a hozzáférési mód a public, private, vagy protected kulcsszavak egyike lehet. Egy mezőhozzáférést módosító kulcsszó hatása egy következő hasonló kulcsszóig, vagy az őt tartalmazó blokkzáró kapcsos zárójelig tart. Pl.: class valami { public: int a,b; bool c; private: float d; public: valami{cout<<"letrejott";}; ~valami{cout<<"megszunt";}; }

Általános elvként tekinthetjük azt, hogy az adatmezők privátak vagy védettek és a csak belső műveleteket végrehajtó függvénymezők szintén privátak, míg a külvilággal való kapcsolattartást publikus függvénymezők biztosítják. Ezért kell a class használata, hiszen a class alapértelmezés szerinti mezőhozzáférési szintjei pontosan a fenti kívánalmaknak felelnek meg. A private-ként deklarált mezőket csak az ugyanabban az osztályban deklarált függvénymezők érhetik el. A protected mezőket az ugyanabban az osztályban, és az adott osztályból származtatott további osztályokban definiált függvénymezők érhetik el. A public mezők korlátozás nélkül elérhetők mindenhonnan, ahonnan az adott osztály is elérhető.

Mezőhozzáférés és öröklés A mezőhozzáférés és az öröklés egymással szoros kapcsolatban állnak. Ennek áttekintéséhez a tanar típusnak a szemely-ből levezetett változatát használjuk úgy, hogy már a mezőhozzáférést is szabályozzuk. class szemely { protected: string nev, szulido; public:void megjelen(int sor);}; class public tanar : szemely { protected: string dszam, szak1,szak2; public: void megjelen(int sor);}; Ez azt eredményezi, hogy a tanar osztály változtatás nélkül örökli a szemely osztaly mezőit és a hozzáférési szinteket is.

Egy származtatott típust általában a következõképpen deklarálhatunk: class D : hozzáférés-módosító B {... }; ahol D a származtatott osztály típusazonosítója, B pedig az alap osztály típusazonosítója. A hozzáférés-módosító vagy public, vagy private lehet; használata opcionális. A class esetén a hozzáférés-módosító alapértelmezés szerint private. Egy származtatott osztály mezőihez való hozzáférést csak szigorítani lehet az alaptípus egyes mezőinek hozzáférési szintjeihez képest. Ha a hozzáférés-módosító a private, akkor az összes protected és public mező is private lesz. Ha a hozzáférés-módosító a public, akkor a származtatott típus változás nélkül örökli az alaptípus mezőhozzáférési szintjeit. Egy származtatott típus az alaptípus minden mezőjét örökli, de azok közül csak a public és a protected mezőket használhatja korlátozás nélkül. Az alaptípus private mezői a származtatott típusban direkt módon nem érhetők el. Használni kell a rájuk vonatkozó függvényeket