prog-2-00

December 3, 2017 | Author: Biczó Dezső | Category: N/A
Share Embed Donate


Short Description

Download prog-2-00...

Description

Alexin Zoltán Programozás II., egyetemi előadás fóliák

114 lap

Programozás II. Egyetemi előadás fóliák a Szegedi Tudományegyetem programozó II. programozó közgazdász II., mérnök informatikus II., fizikusinformatikus II. és számítástechnika tanár II. évf. szakos hallgatók számára

Készítette: Alexin Zoltán (2011) (C.) Copyright, Alexin Zoltán 2011, 2015

Szegedi Tudományegyetem Informatikai Tanszékcsoport 2011.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

2/114

Programozás II., nappali és levelező tagozat, A kurzus teljesítésének feltételei I. Évközi számonkérés: Elérhető maximális pontszám: 100 pont (a) Előadáson teljesitendő egy írásbeli dolgozat (teszt), időpontja: 7-8 héten Elérhető maximális pontszám: 40 Teljesitendő minimális pontszám: 16 (40%) Megjegyzés: amennyiben a hallgató nem szerzi meg a minimális pontszámot egy javítási lehetőséget kell biztosítani (b) Egy projekt munka (kötelező program), a bemutatás határideje: december elején, gyakorlatvezetővel egyeztetett időpontig Elérhető maximális pontszám: 40 Teljesitendő minimális pontszám: 20 (50%) Megjegyzés: a gyakorlatvezetők a kurzus elején megadják a pontozási feltételeket, a pontszám a vizsgajegybe nem számit be! (c) Gyakorlati vizsga (programírás számítógép előtt) időpontja: utolsó hetek egyike – nélküle nem lehet vizsgázni Elérhető maximális pontszám: 60 Teljesitendő minimális pontszám: 24 (40%) Megjegyzés: amennyiben a hallgató nem szerzi meg a minimális pontszámot egy javítási lehetőséget kell biztosítani

II. Kollokvium Elérhető maximális pontszám: 100 Teljesitendő minimális pontszám: 50 Megjegyzés: amennyiben a hallgató nem szerzi meg a minimális pontszámot két javítási lehetőséget kell biztosítani Az elérhető összes pontszám: 200 (I. és II.) Mind a négy alkalommal el kell érni mindenben a minimumot, (ez 90 pont) továbbá még 10 pontot a 2-es osztályzathoz. Jegyek ponthatárai: 170–200 jeles (85%-) 150–169 jó (75%-) 130–149 közepes (65%-) 100–129 elégséges (50%) – 99 (vagy valamelyik topicbol minimum alatti ismételt teljesítés esetén) elégtelen

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

3/114

Ajánlott irodalom •

Herbert Schildt: C/C++ Referenciakönyv Panem Kft Budapest (1998)



Herbert Schildt: C/C++ Programmer’s Reference (3rd extended edition) Osborne McGraw-Hill (2002) ISBN: 0072227222



Bjarne Stroustrup: The C++ Programming Language (special 3rd edition), Addison-Wesley (2000), ISBN: 0201 700735



Scott Meyers: Hatékony C++, ISBN: 9639193828, Scolar Kiadó Budapest, (2003)



A. Koenig, B. E. Moo: Accelerated C++ (Practical Programming by Example) Addison-Wesley (2000) ISBN: 0-201-70353-X



C. Hughes, T. Hughes: Mastering the Standard C++ Classes: An Essential Reference, New York, (1999)



Clovis L. Tondo, Bruce P. Leung: C++ Primer Answer Book Massachusetts, (1999)



Clovis L. Tondo, Scott E. Gimpel: C Programozási feladatok megoldásai Műszaki Könyvkiadó Budapest (1995)



Kris Jamsa: C++ Kossuth Könyvkiadó Budapest (1997)



James O. Coplien: Advanced C++ Programming Styles and Idioms Addison-Wesley (1992)



B. W. Kernighan, D. M. Ritchie: A C Programozási Nyelv Műszaki Könyvkiadó Budapest (1985)



Robert Sedgewick: Algorithms in C Addison-Wesley (1990)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

4/114

Tartalomjegyzék Ajánlott irodalom ........................................................................................................................... 3 I. Bevezetés: az objektum-orinetált programozás alapjai .......................................................... 6 Az öröklődés ................................................................................................................................ 7 Az objektumok egymáshoz való viszonya................................................................................... 9 Az objektum-orinetált programozás előnyei.............................................................................. 10 II. A C/C++ programozási nyelv ................................................................................................. 11 III. C/C++ erőforrások az egyetem oktató kabineteiben .......................................................... 12 Változások a C++ nyelv szintaxisában ...................................................................................... 14 IV. C/C++ érvényességi tartomány típusok ............................................................................... 17 A függyény-prototípus scope ..................................................................................................... 18 A blokk scope ............................................................................................................................ 19 A függvény scope ...................................................................................................................... 20 A global scope ........................................................................................................................... 21 Hogyan használhatjuk a C-ben megszokott header fájlokat C++-ban? .............................. 23 A fájl scope ................................................................................................................................ 24 A blokk és a fájl scope átlapolása ......................................................................................... 26 V. A névtér (namespace) scope .................................................................................................... 28 VI. A class utasítás és a class scope ............................................................................................. 33 Az osztály tagjainak láthatósága ................................................................................................ 35 Hogyan nevezzük el az osztályok tagjait? ................................................................................. 37 Különbségek a C és a C++ között, struct, union?...................................................................... 38 VII. Beágyazott osztályok, típusok (embedded classes and types) .......................................... 41 Az osztályok implementációja................................................................................................... 42 VIII. Az öröklődés (inheritence) ................................................................................................. 49 A viruális öröklődés (virtual inheritence) .................................................................................. 51 IX. Objektumok életciklusa (létrehozás és megszüntetés), konstruktorok és a destruktor .. 52 A konstruktorok ......................................................................................................................... 53 Az ős osztály konstruktorának kézi hívása ............................................................................ 56 A destruktor ............................................................................................................................... 58 Mikor van szükség konstruktor és destruktor alkalmazására? .................................................. 59 Objektumok klónozása, avagy a (copy) másoló konstruktor ..................................................... 65 X. Az operátor overloading ......................................................................................................... 67 Operátor eljárás megvalósítása osztály tagjaként ...................................................................... 72 Operátor eljárás megvalósítása global scope eljárásként ........................................................... 73 Néhány különlegesebb operátor kiterjesztés.............................................................................. 74 Az inline és const függvények ................................................................................................... 76 A volatile módosító használata .................................................................................................. 83 Az iostream template ............................................................................................................. 84

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

5/114

XI. Virtuális eljárások, absztrakt osztályok, késői hozzárendelés ........................................... 85 Korai és késői hozzárendelés ..................................................................................................... 85 Virtuális eljárások használata .................................................................................................... 87 Pure virtual eljárások és az absztrakt osztályok ........................................................................ 89 XII. A static memberváltozók és metódusok ....................................................................... 97 XIII. A static memberváltozók és metódusok ............................................................................ 97 A static tagváltozók ................................................................................................................... 97 A static metódusok .................................................................................................................... 98 Visszahívható (callback) függvények ........................................................................................ 99 A privilegizált operációs rendszerek működésének alapelvei ............................................... 99 A callback eljárások tulajdonságai ....................................................................................... 99 XIV. Kivételkezelés .................................................................................................................... 102 XVI. Függelék: fontosabb ANSI C eljárások és használatuk................................................. 104 A main() eljárás paraméterei ................................................................................................... 104 A C/C++ prepocesszor utasítások............................................................................................ 106 A szövegkezelés eljárásai (string.h) ........................................................................................ 108 A matematikai könyvtár eljárásai (math.h).............................................................................. 110 Az ANSI C standard stream B/K eljárásai............................................................................... 112

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

6/114

I. Bevezetés 1967 1980-as évek eleje 1980-as évek vége

Simula (Dahl és Nygaard) Smalltalk (Kay, Goldberg és Ingalls) C++ (Stroustrup) Objective-C (Cox) Eiffel (Meyer)

Az objektum-orientáltság paradigma első látásra nem a programozási nyelvet jelenti, hanem új módon történő szoftverfejlesztést. Az objektumokat állapotok és viselkedési tulajdonságaik egyesítésének tekinti, amelyek egy szoros és szabatos interfésszel kapcsolódnak egymáshoz. Elősegíti: az információ elrejtést az újrafelhasználást új analízis és tervezési eljárásokat új programozási stílus kialakulását. Az objektum-orientáltság három alappillére: • Encapsulation & information hiding (magyarul kb. egységbezárás és információ elrejtés) • Reusability, polymorphism & inheritence (magyarul kb. újrafelhasználás, polimorfizmus és öröklődés) • Magasabb fokú absztrakció (az absztrakt adattípus = class)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

7/114

Az objektum-orientált filozófia a következőkön alapul: • Egységbezárás (adat elrejtés) „Döntés arról, hogy milyen modulok szükségesek, a programot úgy kell partícionálni, hogy az adatok a modulok belsejében elrejtve legyenek” • Absztrakt adattípus „Döntés arról, hogy milyen típusokra van szükség, minden típushoz definiálni kell az elvégezhető összes műveletet” • Objektum-orientáltság „Döntés arról, hogy milyen osztályokra van szükség, a közös tulajdonságokat láthatóvá teszi az öröklődés”

Az öröklődés

Bankszámla

Megtakarítási számla

Csekkszámla

Különleges megtakarítási számla

Betétkönyves számla

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Befektetési számla

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

8/114

A gyerek (leszármaztatott, angolul derived class) osztály örökli a szülő osztály (basic class, parent class) attribútumait (ezek az adat tagok), illetve a viselkedését (ezek a metódusok, vagy eljárások). Az öröklődés során lehetőség van a gyermek osztályban: • Új adat tagok, attribútumok (memberek) felvételére, • Új eljárások (metódusok) felvételére, • A szülő osztályban meglévő eljárás viselkedésének (törzsének) módosítására. Példák: Jármű (szülő osztály) – Gépjármű (gyerek osztály) Bankszámla (szülő osztály) – Lekötött betétszámla (gyerek osztály) Windows Ablak (szülő osztály) – Windows Dialógus Ablak (gyerek osztály) Nyomtató (szülő osztály) – Postscript nyomtató (gyerek osztály) Természetes nyelvi mondatban ezt úgy fejezzük ki, hogy: a ... [gyermek osztály] az egy ... [szülő osztály], amely ... [extra tulajdonságok]. Pld. A gépjármű az egy olyan jármű, amelynek a mozgását egy beépített motor biztosítja. A postscript nyomtató, az egy olyan nyomtató, amely a feladatát Postscript lapleíró nyelven is tudja fogadni.

Alapfogalmak: Objektumok Osztályok Absztrakt adattípusok Üzenetek Öröklődés Polimorfizmus (az azonos feladatot ellátó eljárások tetszőleges környezetben ugyanazt a nevet viselik)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

9/114

Az objektumok egymáshoz való viszonya Aggregáció (egyik objektum néhány – meghatározott számú másikból épül fel) Asszociáció (egyik objektum felhasznál egy másik objektumot, az eljárásai törzsében, paraméter átadáskor; a pointerrel történő hivatkozás is használatnak minősül) Öröklődés (egyik osztály speciális esete egy másik osztálynak) A gyermek osztály szinte mindig kifinomultabb; több, jobban kidolgoztt eljárást tartalmaz, ezért helyes rájuk így is gondolni. A programozási nyelv lehetővé teszi az osztályok (absztrakt adattípusok), az öröklődés és a „használat” leírását. A használat során megkülönböztetünk kliens (aki igénybe veszi) és szerver objektumot (aki nyújtja a szolgáltatást). Az adattag nélküli osztályok tekinthetők „hasznos eljárások egy erőforrásának”, amelyekből öröklődéssel számos egyedi osztályt alkothatunk, adattagok hozzáadásával.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

10/114

Az objektum-orientált programozás előnyei Milyen kritériumai vannak a jó programok elkészítésének? 1. Korrektség, verifikálhatóság 2. Robusztusság (figyelem az extrém abnormális esetekre) 3. Bővíthetőség (kiterjesztés és javítás) 4. Újrafelhasználhatóság 5. Integritás (autonóm, zárt komponensek) 6. Kompatibilitás (hasonló programokkal, adatcsere, helyettesíthetőség) 7. Hatékonyság 8. Hordozhatóság 9. Könnyű használhatóság 10. Karbantarthatóság (a karbantartás a szoftver életének nagy részén vonul végig)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

11/114

II. A C/C++ programozási nyelv • Bjarne Stroustrup, Bell Labs. (http://www.hitmill.com/programming/cpp/cppHistory.asp), első implementáció 1983, ötleteket vett át a Simula67-ből (osztályok, származtatott osztályok), Algol68-ból (operátor kiterjesztés), Ada (templatek), Clu, B CPL programozási nyelvekből • Minden C program lefordítható C++ ban is (így futtatható is), azonban az nem garantált, hogy ugyanaz a tárgymodul jön létre! • A C++ a C programozási nyelv magasabb absztrakciót lehetővé tevő objektum-orientált bővítése • Nagy volumenű fejlesztések támogatása, névterek, információ elrejtés, csoportmunka, szigorú típus ellenőrzés

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

12/114

III. Erőforrások az egyetem oktató kabineteiben 1. GNU C/C++ for MSDOS A programfejlesztési környezet az Irinyi oktató kabinetekben a 224 és a 226-os teremben az N: disken található a N:\GNU alkönyvtárban. Mielőtt használni kívánja, el kell indítani az N:\BAT\GNU.BAT eljárást, ami az útvonalakat beállítja. Az N:\GNU\ZIP alkönyvtárban található .ZIP fájlokat minden hallgató hazaviheti és telepítheti a saját gépére. Az itt található GNU C/C++ 2.7.2 egy ingyenes (freeware) programcsomag. Ugyanebben az anyagban megtalálható a GNU Pascal 2.0 is. A fordító program neve gcc.exe, a szerkesztő programé ld.exe, a szövegszerkesztőé rhide.exe. 2. GNU C/C++ for Unix A home.cab.u-szeged.hu (rozi): GNU C/C++ 2.95.3 fejlesztési környezet található. Használatához nincs szükség semmilyen előkészítésre, minden felhasználó indíthatja. A program neve gcc. Gyári Motorola C/C++ fordító program neve: cc Sun WorkShop 6 update 2C5.3 (2001. május 15.) A sirius-on: A GNU C/C++ 2.95.3 verziója van meg (2001. március 15.) – korszerű, fejlesztési környezet. A program neve gcc. A gyári SUN C/C++ fordító program neve cc, de nem működik. A forráskód szerkesztésére Unix operációs rendszerben a következő programok használhatók: vi, xedit, pico, xwpe, nedit, emacs, xemacs.

3. Visual C/C++ 6.0 (illetve a Visual Studio .NET 2005)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

13/114

A programfejlesztési környezet az Irinyi oktató kabinetekben megtalálható. A programmal egyebek mellett 32 bites MS-DOS alkalmazások, Windows (API, MFC, EXE, DLL, Console Application) programok készíthetők. A 32-bites Win 2000/XP operációs rendszert elindítva a programok használhatók, a parancssori fordító használatához szükséges beállításokat a számítógépek elvégzik. A parancssori fordító neve: cl.exe. 4. STL A C++ programozáshoz használható általános célú template könyvtár, mind a siriuson, mind pedig a Visual C/C++ 5.0 (6.0) fejlesztői környezetben használható. A template könyvtár már telepítésre került, a dokumentációja viszont innen: /pub/program/C++/STL_Doc/index.html érhető el. Az STL egy ingyenes (freeware) programcsomag, amit mindenki szabadon használhat olyan ANSI C/C++ fordító programokkal, amelyek képesek lefordítani a benne szereplő defíníciókat. A könyvtár lényegében néhány header fájl, amelyben paraméteres osztályok (templatek) és (template)paraméteres eljárások vannak. 5. Honlap Nemcsak a C/C++, hanem más programrendszerekkel kapcsolatos információk találhatók a honlapomon: http://www.inf.u-szeged.hu/~alexin/. Például: Prolog, funkcionális programozás, Oracle, programok használati rendje, levelezési listák, honlapok. Vannak helyben olvasható dokumentációk, amelyek csak azokon a gépeken olvashatók, amelyekre fel van mountolva a /pub/Programozas-II.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

14/114

Változások a C++ nyelv szintaxisában Minden változás permanens bővítés, ami azt jelenti, hogy a továbbiakban is minden C program lefordítható C++ -ban is. (Fordítva természetesen nem igaz.) A C++ nyelv számára azonban új tárgymodul struktúra volt szükséges, ezért a visszafelé kompatibilitás miatt a fordító programnak tudnia kell, hogy C vagy C++ tárgymodult állítson elő. A fordító a parancs sorból veszi az ehhez szükséges információkat (az input fájl kiterjesztése szerint végez C vagy C++ fordítást), illetve parancssori paraméterekkel is lehet befolyásolni. (cl -TP|C). Itt most a fontosabb változások szerepelnek: • Egysoros megjegyzés. A régi /* és */ jelek közé zárt többsoros megjegyzés továbbra is használható int k ; // ez egy megjegyzés • Blokk utasításban nem kell a típus/változó definícióknak megelőzniük a végrehajtható utasításokat. int sum(int a, int b) { int c ; c = a ; int d ; d = b ; return c+d ; } • A for utasításban létrehozható ciklusváltozó az első paraméter helyén for(char c = 0, *p = "Alma" ; c < (char) strlen(p) ; ++c) { ... }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

15/114

• Az eljárások default paraméterei long int sum(long a, long b = 0, long c = 0) { return a+b+c ; } az eljárás ezután meghívható sum(1) ; sum(2, 5) ; sum(1, 2, 3) ; Az eljárások visszaadott értéke bármilyen típusú lehet struct abc {long tomb [40] ;} ; abc fill(abc in, long f = 0) { for (int i = 0 ; i < 40 ; ++i) in.tomb[i] = f ; return in ; } • Referencia típus (cím szerint átadott var paraméter) int strlen (char &str) { int i ; for (i = 0 ; (&str)[i] != '\0' ; ++i) ; return i ; } • Szigorú típus egyeztetés • Prototípus megkövetelése • Új utasítások: class, template, namespace, ...

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

16/114

Deklarációk szintaxisa a C/C++ nyelvben 1. Deklarációk: típus/attr

; init deklarátor lista

2. Init deklarátor lista: deklarátor =

kifejezés

, 3. Példák: int k, l, m ; int k=1, l=3, m=k+3 ; int a[2], b[3,4], *pint, ifgv(long) ; az utolsó sor az alábbi négy deklarációt rövidíti: int a[2] ; int b[3,4] ; int *pint ; int ifgv(long) ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

17/114

IV. C/C++ érvényességi tartomány típusok (scope) Scope: a forráskód egy pontosan meghatározott része (modul név (fájl), sor, kezdő és záró character pozíció). A scope lehet összefüggő illetve összefüggő forráskód részek halmaza. Egy scope kiterjedhet több modulra, amelyek különböző alkönyvtárakban vannak. A scope-ban definiált programobjektumok élettere általában csak erre a területre terjed ki. prototípus érvényességi kör int

skip_over(FILE *file, char *pattern, long *) ;

int {

skip_over(FILE *file, char *pattern, long *line) typedef unsigned long ULONG ; while (!feof(file)) { char *p = pattern;

formális paraméterek hatásköre az eljárás az ULONG típus hatásköre a külső blokk

while (*p != '\0') { c=fgetc(file); a p változó a if (c == '\n') (*line)++; blokkban érvényes if (c == *p) ++p; else break; } if (*p == '\0') return 1; } return 0; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

18/114

1. A függvény-prototípus scope long int sum(int a, int b) ; long int sum(int x, int y) ; void * (*proto_type(long int, char *b)) (const char *b, unsigned short int d) ; long int sum(int a, int a) ;

helytelen

int record_func(struct A {int a; long b;} *param) ; // a struct A csak a prototípusban érvényes! A függvény-prototípus scopeban használt azonosítók érvényessége a függvény paramétereket közrezáró zárójelek közötti forráskód részlet. void * (*proto_type(long int, char *b)) (const char *b, unsigned short int d) ; • A prototípusban a paramétereket nem kötelező elnevezni, • Ha azonban adok nevet, akkor egy paraméterlistában nem lehet név ütközés, • Ha később ismét definiálom egy függvény prototípusát, más paraméter azonosítókkal, az megengedett. A fordító csak a típusokat tárolja.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

19/114

2. A blokk scope A C/C++ programozási nyelv leggyakrabban előforduló érvényességi tartomány típusa. A block scopeban definiált programobjektumok érvényességi köre a blokk-utasítást határoló { és } (kapcsos zárójelek) közötti terület. Egy belső blokkban megismételt azonosítóval létrehozott programobjektum eltakarja a külső blokkbeli ugyanilyen nevű programobjektumot. Egymásba ágyazott blokkok esetén belűlről kifelé haladva keresi meg a fordító a megadott azonosítójú programobjektumot. A blokkba belépve létrejönnek a blokkban definiált programobjektumok, a blokkból kilépve megszűnnek. Az egymásba ágyazott blokkoknak megfelelő programobjektumok létrehozásának és megszüntetésének technikai megvalósítása veremmel történik. Ennek következménye, hogy a blokkból kilépve, az ott definiált programobjektumok fizikailag is megszűnnek létezni (a helyüket újabb programobjektumok foglalhatják el). if (pInt != NULL) { int a ; if (k != 4) { double a ; a = 6.26 ; } a = 3 * 2 ; } else { char a[20] = “abcdefg” ; printf(“%s\n”, a) ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

20/114

3. A függvény scope A C/C++ programozási nyelv eljárásaiban a formális paraméterek és a goto címkék érvényességi tartománya az a teljes eljárás (fej és törzs), amelyben azokat definiálták. Ennek két fontos hatása van: • a formális paramétereket a blokkokban definiált lokális változók elfedhetik • a blokkok között szabadon lehet ugrani a goto utasítással, azonban az eljárásból kiugrani nem lehet. Egy vezérlési szerkezetből (ciklus, feltételes utasítás) kiugrani megengedett, ellenben egy vezérlési szerkezet belsejébe beugrani nem logikus és rossz programozási stílus. int strlen (char *p) { int len = 0 ; char *q = p ; if (p == NULL) goto exit ; len = 0 ; while(1) { long p ; p = 4 * 12 ; if (*q == '\0') ++q ;

goto end ;

} end : return len ; exit: return 0 ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

21/114

4. A global scope A global scope, a több modulból álló projekt egész területét jelenti. A C/C++ programozási nyelvben az (ún. globális) eljárások és az eljárástörzsön kívül definiált (ún. globális) változók érvényességi tartománya ez. Ez azt jelenti, hogy ha másként nem rendelkezik a programozó, akkor egy projekten belül minden globális változó és globális eljárás korlátozás nélkül látható, használható, illetve meghívható. /* a.c forrás fájl int a ; void setA(int b) { a = b ; }

*/

/*

*/

b.c

forrás fájl

#include extern int a ; extern void setA(int) ; int main() { a = 67 ; printf("a = %d\n", a) ; // az a a másik modulból setA(88) ; // setA a másik modulból printf("a = %d\n", a) ; } Fordítás és futtatás: c:>cl a.c c:>a.exe a = 67 a = 88

b.c

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

22/114

Tárolási osztályok a C nyelvben: register - a CPU egy regiszterében auto - veremszegmensben (csak eljáráson belül) static - adatszegmensben, lokálisan extern - adatszegmensben, globálisan (tárgymodulban extern-ként) a default: - adatszegmensben, globálisan (tárgymodulban public-ként) pl. auto int s ; register long k ; static char s[30] ; long int u ; A global scope érvényességű programobjektumok tárolási osztálya az extern volt. C++-ban az extern modifierrel jelezzük, hogy az adott program objektum a global scope-hoz tartozik. A programobjektum definiálásakor nem írunk extern-t (ez alapértelmezés – automatikus). Ha kitesszük, akkor az azt jelzi, nem szükséges helyfoglalás, a programobjektum számára már korábban foglaltak helyet egy másik modulban. A C++ programozási nyelvben két új tárolási osztályt vezettek be a régebbi extern helyett az extern ”C” és az extern ”C++”-t. Az utóbbi az alapértelmezett. extern ”C” long int Sum(int a, int b) ; és az extern ”C++” long int Sum(int a, int b) ; vagy röviden: long int Sum(int a, int b) ; Ez a két eljárás C++ programozási nyelvben különbözik.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

23/114

Hogyan használhatjuk a C-ben megszokott header fájlokat C++ban? A C-ben megismert és használt standard C header fájlok továbbra is használhatók C++-ban. Ehhez kisebb változtatást kellett rajtuk végrehajtani, amely után lehetséges a további használat. A C/C++ fordító program egy belső ún. define makró segítségével jelzi, hogy az éppen fordított modul C vagy C++ nyelvű. A makró neve: __cplusplus. A standard header fájlok így néznek ki: #ifndef STDIO_H #define STDIO_H #ifdef __cplusplus extern ”C” { #endif korábbi C nyelvű deklarációk pld. FILE * fopen (const char *, const char *) ; int fclose(FILE *) ; ... #ifdef __cplusplus } #endif #endif

// STDIO_H

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

24/114

5. A fájl scope Amennyiben nem kívánjuk, hogy a programobjektumaink a global scope-ba kerüljenek, akkor helyette használhatjuk a fájl scope érvényességi tartományt. Ezt a static módosító (modifierrel) alkalmazásával érhetjük el. Az adott programobjektum neve és címe ilyenkor nem kerül be a tárgymodulba, ami kizárja, hogy egy másik modul azonos nevű programobjektumával ütközés léphessen fel. static int Sum(int a, int b) { return a+b ; } static char filename[_MAX_PATH] ; Tárgymodulok felépítése: • publikus szimbólumok (assemblyben: public) táblázata. Ebben a táblázatban a modulban definiált objektumok neve és eltolási címe. Lehet rájuk más modulból hivatkozni, vagy meghívni az eljárásokat. • hivatkozott szimbólumok (assemblyben: extern + FIXUP lista). A modulban hivatkozott objektumok neve, amelyek definíciója nem található meg a modul forrásában. Az ismeretlen címek helyére 0x00 bájtokat tesz a fordító. A helyet, ahová az érvényes címet szerkesztéskor kell majd tenni, megjegyzi a FIXUP listán. • gépi kód (bájtkód). Az ismeretlen címek helyén 0x00 bájtok állnak, a szerkesztő program javítja cseréli ki a szerkesztéskor megállapított érvényes eltolási címekre.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

25/114

A tárgymodul belső felépítése FIXUP lista, az ext1 szimbólum címét hová kell beilleszteni. szimb1 szimb2 . . szimbn

Header public szimbólumok

extern szimbólumok

_TEXT segment proc szimb1 (bájt kód) … call ext2 proc szimb2 … call ext1 _DATA segment (adat bájtok) szimb4 DW ?

0x0010 0x016A . . 0x0CB6

ext1

0x0077 knd1 . . 0x009F kndn1

ext2

0x01A5 knd2 . 0x01DF kndn2

… extk

0x034B kndk . . 0x04CE kndnk

A kndi mutatja, hogy a cím mely részét kell beírni a megadott helyre, szegmenscím, 16 bites eltolási cím, 32-bites eltolási cím, 16bites relatív cím, stb.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

26/114

A blokk és a fájl scope átlapolása A C/C++ programozási nyelv blokkjaiban lehetőség van fájl scope érvényességű változók definiálására. if (k == 0) { static int s = 0 ; ... } else { static int s = 1 ; } Ebben az esetben egy olyan változó jön létre, amely viselkedésében sztatikus, (a program teljes futási idejében létezik), viszont érvényességi köre csak blokk scope. Ez azt jelenti, hogy a két ugyanolyan nevű (s) változó az adatszegmens két különböző című helyén tárolódik (valóban két teljesen különböző változó lesz). Fontos tudni(!), hogy az inicializálás nem hajtódik végre csak egyszer, akkor amikor a fordítóprogram helyet foglal a változónak, a blokkba való belépéskor már nem. Az s változók megőrzik az értéküket, amit a blokkban való legutóbbi tartozkodáskor felvettek. Ha ismét belépünk a blokkba ezek rendelkezésre állnak. A static változók címét tetszés szerint továbbíthatjuk a programban (a változók mindig elérhetőek, nem úgy mint a veremben tárolt lokális változók).

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

27/114

void f() { static int i = 0 ; } void g() { static int i = 0 ; } Az f() és a g() eljárásokban használt két változó különböző lesz! Ezért a fenti példában nem lehet közös adatterületként használni őket. Helyette a következő megoldást képzelhető el: static int i = 0 ; void f() { használom az i változót } void g() { használom az i változót }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

28/114

V. A névtér (namespace) scope [C++] A C++ programozási nyelvben egy új szolgáltatás áll a programozók rendelkezésére nagy rendszerek készítéséhez: ez a névtér. A névtér egy hierarchikus programobjektum-azonosító tárolási struktúra (vö. mappák, folderek). A programobjektumok elhelyezése a névterekben egyszerű. A keresési útvonal programozható. A megkezdett névtér folytatható, a folytatások száma nincs korlátozva. Szintaxis: namespace ::> namespace [identifieropt] { namespace-törzs } namespace A { int a ; namespace B { int a ; short C(short k) ; } } namespace A { int a ; // hiba: azonosító ütközés int b ; long C(int k) ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

29/114

A névtérbeli programobjektumokra történő hivatkozás: A::B::a. A hivatkozásnak teljesnek kell lennie, a névtér hierarchia minden azonosítójának kell benne szerepelni. Az azonosítókat a scope-resolution (scope feloldó) operátor (a ::) választja el egymástól. A global scope szintén egy névtér, amit jelezhetünk így ::Sum(23, 23) ; (az azonosítók elé tett :: jellel). A névtérbeli eljárások definíciója: long A::C(int k) { return k+1 ; } short A::B::C(short k) { return k+1 ; } A névtér implementációja: a tárgymodulba kimentett azonosítók megváltoztatásával. Lásd később. Keresési útvonal megadása: a using utasítással. Egymás után több using utasítást is írhatunk. A nem egyértelmű hivatkozások elkerülése a teljes útvonal megadásával mindenképpen javasolt. Szintaxis: using ::> using namespace scope ; Például: using namespace D ; using namespace E::F::G ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

30/114

namespace D { int d1; void f(int); void f(char); } using namespace D; int d1;

// nincs ütközés a D::d1 -gyel

namespace E { int e; void f(int); } namespace D // namespace folytatása { int d2; using namespace E; void f(int); } void f() { d1++; ::d1++; D::d1++; d2++; e++; f(1); f('a'); }

// // // // // // //

hiba, melyik ::d1 or D::d1? ok ok ok: D::d2 ok: E::e melyik D::f(int) or E::f(int)? ok D::f(char)

Ez a példa a Microsoft Visual C/C++ 5.0 (6.0) MSDN Helpben a using utasításnál megtalálható.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

/* a.cpp programfájl namespace Gyar { namespace Telep { int k ; int l ; int sum_kl() ; } }

31/114

*/

minősített eljárásnév

int Gyar::Telep::sum_kl() { return k+l ; } /* b.cpp programfájl #include

*/

namespace Gyar { namespace Telep { extern int k ; extern int l ; extern int sum_kl() ; } }

hivatkozás másik modulban definiált programobjektumokra

Nem kell kiírni a Gyar::Telep:: prefixet

using namespace Gyar::Telep ; int main() A Gyar::Telep::k { helyett elég csak a k k = 56 ; l = 45 ; printf("sum_kl() = %d\n", sum_kl()) ; } fordítás: c:>cl a.cpp b.cpp futtatás: c:>a.exe sum_kl() = 101 Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

32/114

/* az a.cod fordítási lista fájl */ ; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.40219.01 TITLE H:\H-Disk 20090326\VCProjects\a.cpp .686P extern ”C++” a tárgymodulból .XMM azonosítók kilátszik a két azonosító include listing.inc képzése .model flat PUBLIC ?l@Telep@Gyar@@3HA ; Gyar::Telep::l PUBLIC ?k@Telep@Gyar@@3HA ; Gyar::Telep::k _BSS SEGMENT /* Begin of Stack Segment */ ?l@Telep@Gyar@@3HA DD 01H DUP (?) ; Gyar::Telep::l ?k@Telep@Gyar@@3HA DD 01H DUP (?) ; Gyar::Telep::k _BSS ENDS PUBLIC ?sum_kl@Telep@Gyar@@YAHXZ ; Gyar::Telep::sum_kl ; Function compile flags: /Odtp ; File h:\h-disk 20090326\vcprojects\a.cpp _TEXT SEGMENT ?sum_kl@Telep@Gyar@@YAHXZ PROC ; Gyar::Telep::sum_kl ; 13 : { 32-bites eltolási cím. 00000 55 push ebp 00001 8b ec mov ebp, esp A cím nincs kitöltve ; 14

:

return k+l ;

00003 a1 00 00 00 00 mov eax, DWORD PTR ?k@Telep@Gyar@@3HA ; Gyar::Telep::k 00008 03 05 00 00 00 00 add eax, DWORD PTR ?l@Telep@Gyar@@3HA ; Gyar::Telep::l ; 15 : } 0000e 5d pop ebp 0000f c3 ret 0 ?sum_kl@Telep@Gyar@@YAHXZ ENDP ; Gyar::Telep::sum_kl _TEXT ENDS END

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

33/114

VI. Az class utasítás és a class scope [C++] A C++ programozási nyelv lehetőséget biztosít absztrakt adattípus (osztály) definiálására. Az osztály adat tagokat tartalmaz, amelyek az osztály példányainak számítógépes reprezentációját teszik lehetővé (korábban definiált típusú változókkal), valamint eljárás tagokat (metódusokat), amelyek az osztály egyes példányain műveleteket végeznek. Az osztály belsejében definiált program objektumok érvényességi köre leginkább a névtér érvényességi körére hasonlít. Az osztály utasítás felfogható egy speciális névtérnek. Az osztály névtér azonban nem folytatható, azaz egy ugyanolyan nevű osztály felbukkanása a forrás egy későbbi részében szintaktikus hiba még akkor is, ha ugyanazok a memberek. Az osztály utasítás rendelkezik egy különleges tulajdonsággal – ez a példányok létrehozásának képessége (instantiation). Minden osztályból tetszés szerinti mennyiségben példányokat, objektumokat hozhatunk létre, csak a lefoglalható memória mérete szab korlátot. Minden példány (angolul instance) egyedi attribútimokkal rendelkezik (mégha azok esetleg azonosak lennének is egy másik objektum attribútumaival), viszont a példányok osztoznak az eljárásokon. Azt is mondhatjuk, hogy a metódusok az osztályhoz és nem az egyes objektum példányokhoz tartoznak. Minden objektum egy azonosítóval kell rendelkezzen (az is egy program objektum). Egy objektumbeli adatra az objektum példány azonosítójával és a member azonosítójával együtt kell hivatkozni (pl. aObj.bMemeber). Az osztály tagjainak (angolul memberek) elérhetősége szabályozható, azaz az objektum azonosító és a member azonosító birtokában sem feltétlenül lehet az objektum példány egy adatára hivatkozni. Minden memberre egyenként megadható egy láthatósági opció, ami ezt szabályozza.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

34/114

A class utasítás egyszerűsített szintaxisa: class-utasítás ::> class [ tag [ : base-list ]] { member-list } [declarators] ; class-utasítás ::> [ class ] tag declarators ; például: class A : B, std::C { FILE *m_file ; void open(char *file) ; void close() ; } ; vagy class B::D::Cobject

*p, aProc() ;

Az első példában az A osztálynak két ős osztálya van a B és az std::C (az std vagy egy névtér vagy egy a C osztályt tartalmazó másik osztály). Az két ősosztálytól örökölt membereket az ott felsorolt új adat-taggal (FILE *m_file) és két új (vagy módosított) eljárással egészítjük ki. Az osztályokat a memberek felsorolása nélkül is használhatjuk objektumok létrehozására, ha a program forrás-szövegében erre később sor kerül (vagy nem használunk egyetlen membert sem pld. csak egy pointer típust szeretnénk létrehozni). Ez már C-ben is ismert volt a struct és union utasításoknál. Ha pointerezett listát kellett készítenünk, akkor a pointer-típust még a struct mezőinek megadása előtt kellett definiálnunk egy mezők nélküli rekord-típus segítségével: typedef struct listaTAG *pLista ; typedef struct listaTAG { pLista pFollow ; typeDATA data ; } Lista ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

35/114

Az osztály tagjainak láthatósága Az osztályok minden egyes tagját egyenként szabályozható módon lehet ellátni az alábbi három láthatósági opcióval. Az alapértelmezés a private láthatóság. A láthatósági opció meggátolhatja egy adat (eljárás) tag elérését még abban az esetben is, ha mind az objektum azonosítója mind pedig a member neve ismert. private: az ilyen tagra csak az osztály scope-on belül lehet hívatkozni. protected: az ilyen tagra csak az osztály scope-on (és a jövőben ebből származtatott gyermek osztályokon) belül lehet hivatkozni. public: az ilyen tagra bárhonnan lehet hivatkozni. Az objektum orientált programozásban az alábbi ajánlás fogalmazható meg. Törekedjünk a maximális adatbiztonságra és információ elrejtésre. Az adat tagok láthatósága private (esetleg nagyon indokolt esetben protected) legyen; az interfészben szerepeltetni kívánt metódusok láthatósága legyen public, egyébként protected. Az ős osztály private eljárásait nem lehet a gyermek osztályokban használni. A láthatóság implementációja: fordítási időben történik ellenőrzés. A futó kódban már nincs semmilyen védelem, azaz pointeres trükkökkel megkerülhető.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

36/114

// példa class BaseClass { protected: int protectFunc() ; }; class DerivedClass : public BaseClass { private: long m_long ; void *m_pVoid ; char m_szText[20] ; public: int useProtect() { protectFunc(); } // protectFunc elérhető // a gyerek osztályból } ; void main() { BaseClass aBase; DerivedClass aDerived; aBase.protectFunc();

// protectFunc nem // elérhető aDerived.protectFunc(); // protectFunc nem // elérhető a gyerek osztályban aDerived.m_pVoid = NULL ; // nem elérhető }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

37/114

Hogyan nevezzük el az osztályok tagjait? Ajánlás: az adatmemberek elnevezése mindig m_ -ral kezdődjön, mert ez megkülönbözteti őket a típusoktól és eljárásoktól illetve a lokális és globális (global vagy fájl scope) változóktól. Az elnevezések tükrözhetik a illető member típusát is. Ezt a Microsoftnál vezették be, és magyar jelölésnek (Hungarian notation) nevezik, mert egy magyar származású informatikus találta ki. Az egyes típusok egy betűjelölést kapnak: b

boolean, logikai

i

Integer

l

Long

sz

C sztring (\0-val a végén)

p

Pointer

ui

unsigned int

ul

unsigned long

a

Tömb

n

számláló (short int)

h

HANDLE

s

Sztring

A metódusok neve világosan tükrözze a feladatát; ha szükséges használjunk tetszőlegesen hosszú eljárás neveket, ami még nem megy az érthetőség rovására (a fordítók meg szoktak engedni 256, 512 ... character hosszú azonosítókat).

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

38/114

Különbségek a C és a C++ között, struct, union? A C++ programozási nyelvben a struct és a union is osztály lett. (A union mezői (adat tagjai) továbbra is mind azonos memória címen fognak kezdődni.) Ennek következményei a következők: • a típus használatakor nem kell kiírni a struct illetve a union kulcsszót például: struct A { int k ; } ; main() { struct A aRec ; // C++-ban elég az: A aRec ; } • A megkezdett struct, union (és class) típus neve már használható a memberek definíciójához struct A { A *m_pNext ; int m_Key ; } ; • A struct-nak és a union-nak is lehetnek metódusai, minden egyes memberhez láthatósági opció rendelhető • A struct és a union tagjainak alapértelmezett láthatósága public, a class memberek alapértelmezés szerinti láthatósága private • A union nem lehet ős osztály, és nem lehet leszármaztatott osztály

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

/*

d.c programfájl

#include

39/114

*/ Az union A típus különbözik a typedef-fel létrehozott A típustól.

union A { int k ; long l ; } ; typedef long int A ; int main() { union A A z ;

az union/struct/enum típus használatakor az union kulcsszót ki kell írni.

aUni ;

Az A típus egy typedef-fel létrehozott típus neve!

return 0 ; } A C nyelv a struct, enum és union típusokat a typedef utasítással létrehozott típusoktól elkülöníti. (Külön polcra/fiókban helyezi el.) Ez a program hiba nélkül fut le C nyelvben: C:\>cl d.c Microsoft (R) 32-bit C/C++ Compiler for 80x86 Copyright (C) Microsoft Corporation. d.c Microsoft (R) Incremental Linker Version 10.00.40219.01 Copyright (C) Microsoft Corporation. /out:d.exe d.obj

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

/*

d.cpp programfájl

#include union A { int k ; long l ; } ; typedef long int A ; int main() { union A A z ;

aUni ;

40/114

*/ C++-ban az union A típus már nem különbözik a typedef-fel létrehozott A típustól.

az union (struct, enum) típus használatakor az union kulcsszót már nem kell kiírni.

Az A típus lehet egy enum, struct, union, class, vagy typedef típus is!

return 0 ; } Ez a program C++-ban hibás lesz, mert ugyanazzal a névvel egy másik típust is létre szeretne hozni a program, ami természetesen hibaüzenetet eredményez. Ez egy ritka kivétel, amikor a C program nem fordul le C++-ban! C:\>cl d.cpp Microsoft (R) 32-bit C/C++ Compiler for 80x86 Copyright (C) Microsoft Corporation. d.cpp d.cpp(10) : error C2371: 'A' : redefinition; different basic types d.cpp(5) : see declaration of 'A'

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

41/114

VII. Beágyazott osztályok és típusok (embedded classes and types) Az osztályok névtér tulajdonságából következik, hogy az egyes osztály definíciók egymásba lehetnek ágyazva. Azokat az osztályokat hívjuk beágyazott (embedded) osztályoknak, amelyek egy másik osztály belsejében vannak definiálva. Ilyenkor a definíciók (class B és class C) az A osztály scope-jában lesznek és nem kerülnek kölcsönhatásba a program többi részével. Az osztályok egymásba ágyazásának azonban semmilyen kapcsolata sincs azzal, hogy az objektum példányok szintjén is megvalósul-e aggragáció vagy sem. class A { class B { } ; class C { } ; } ; A fenti példában bemutatott B és C osztály felhasználható az A osztály metódusainak implementálásához. Lévén beágyazottak, bizonyos védelmet élveznek az A osztály belsejében. Általánosan is igaz, hogy typedef (enum) típusdefiníciókat ágyazhatunk be egy osztály belsejébe, amelyek az alábbi tulajdonságokkal rendelkeznek: • Láthatóságuk a legújabb C++ rendszerekben (Visual C/C++ 6.0) már szabályozható (régebbi rendszerekben public volt). • Kívülről hivatkozni a típusazonosítóra a teljes scope feltüntetésével lehet (pl. A::B::dataType, és ez az enum azonosítókra is vonatkozik pl. ColorWindow::red, ColorWindow::green)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

42/114

Az osztályok implementációja Az osztályok (a class utasítás) típusdefinícióként viselkedik. Az osztályból létrehozott példányok működése csak akkor képzelhető el, ha a metódusok eljárás törzsei is rendelkezésre állnak. A metódusok kódolása jelenti egy osztály implementációját. Követve a C-beli konvenciókat, a modulok interfész részében szereplő típusdefiníciók helye a header fájl – itt legyen tehát a class utasítás. Az eljárások kódolása pedig a forrás (.cpp, .cxx, .CC) fájlokban történjen meg. Ezek szerint egy projekt a következő fájlokból fog állni: main.cpp, class1.h, class1.cpp, class2.h, class2.cpp, class3.h, class3.cpp, stb. Az egyes forrásprogram fájlok igény szerint #include-olják a szükséges header fájlokat, amelyek a metódusok implementációjához szükségesek. A C++ nyelv is megengedi, hogy a header fájlok tartalmazzanak (eljárás) forráskódot is. Ezeket régen inline eljárásoknak nevezték (nem tévesztendő össze az inline eljárásokkal, lásd később!) Ez a technika azonban nem ajánlott, esetleg a nagyon kis törzsű (leginkább az inline) eljárások lesetén (2-4 rövid sor) fogadható el. Normálisan a metódusok törzsének a helye a .cpp fájl. Mi lesz a futtatható program neve. A default: a.out (a.exe) A projekt fordítása: gcc –o prog main.cpp class1.cpp class2.cpp class3.cpp vagy cl –FePgm.exe main.cpp class1.cpp class2.cpp class3.cpp Mi lesz a futtatható program neve. A default: main.exe (első forrás)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

43/114

Több modulból álló program fordítása: a.) A modulok C/C++ forrásban állnak rendelkezésre. Ebben az esetben a fordítóprogram egyesével lefordítja a forrásfájlokat, a kapott tárgymodulokat pedig egy második lépésben futtatható programmá szerkeszti össze. b.) A modulok részben tárgymodulok formájában, részben forrásprogram formájában állnak rendelkezésre. A fordítóprogram parancssorában meg lehet adni tárgymodulokat is, azokat nem fogja lefordítani (a kiterjesztésükről ismeri fel őket). A forrásfájlokat a fordító lefordítja és kapott tárgymodulokat, a felsorolt további tárgymodullal együtt egy futtatható programmá szerkeszti össze. c.) A modulok részben tárgymodulok formájában, részben forrásprogram formájában állnak rendelkezésre. A lefordított tárgymodulokat egy tárgymodul-könyvtár fájlban egyesíthetem. A fordító programnak megadom a forrásfájlokat és a tárgymodul-könyvtár fájl(oka)t. A fordító a C/C++ forrásfájlokat lefordítja. Utána futtatható programot készít a megadott forrásfájlok tárgymoduljaiból és a tárgymodulkönyvtár fájlban található tárgymodulokból. d.) Ha minden modul tárgymodul formájában van jelen, akkor elegendő ezeket fordítás nélkül, a szerkesztő programmal futtatható programmá szerkeszteni. Statikus tárgymodulkönyvtár fájlok:

HDR

module1.obj

modulen.obj



Kiterjesztésük .lib (Visual Studio) vagy .a (UNIX/Linux).

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

44/114

Több forrásfájlból álló program fordítása Visual Studióval példaként a korábban bemutatott a.cpp és b.cpp fájlokat használva: a.) Forrásfájlokból kiindulva: C:\>cl a.cpp b.cpp Microsoft (R) 32-bit C/C++ Compilerfor 80x86 Copyright (C) Microsoft Corporation. a.cpp b.cpp Generating Code... Microsoft (R) Incremental Linker Copyright (C) Microsoft Corporation. /out:a.exe a.obj b.obj

működik fordítva is: cl b.cpp a.obj

b.) Tárgymodul és forrásfájl felhasználásával: C:\>cl a.cpp b.obj Microsoft (R) 32-bit C/C++ Compiler for 80x86 Copyright (C) Microsoft Corporation. a.cpp Microsoft (R) Incremental Linker Copyright (C) Microsoft Corporation. /out:a.exe a.obj b.obj

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

45/114

A tárgymodul-könyvtár készítő program neve: lib.exe c.) Tárgymodulkönyvtár és forrásfájl felhasználásával: C:\>lib /out:liba.lib a.obj Microsoft (R) Library Manager Version 10.00.40219.01 Copyright (C) Microsoft Corporation. Fordítás C:\>cl b.cpp liba.lib Microsoft (R) 32-bit C/C++ Compiler for 80x86 Copyright (C) Microsoft Corporation. b.cpp Microsoft (R) Incremental Linker Copyright (C) Microsoft Corporation. /out:b.exe b.obj liba.lib

Mi lesz a futtatható program neve A –c jelentése: csak fordítás, szerkesztés nem.

d.) Csak tárgymodulok felhasználásával:

C:\>cl -c a.cpp b.cpp Microsoft (R) 32-bit C/C++ Compiler for 80x86 Copyright (C) Microsoft Corporation. a.cpp b.cpp Generating Code...

A szerkesztő program neve: link.exe A futtatható program neve: a.exe (első tárgymodul)

C:\>link a.obj b.obj Microsoft (R) Incremental Linker Copyright (C) Microsoft Corporation.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

46/114

Több forrásfájlból álló program fordítása Solaris operációs rendszerben GNU C/C++ fordítóval példaként a korábban bemutatott a.cpp és b.cpp fájlokat használva: a.) Forrásfájlokból kiindulva: alexin@home:~/testing$ gcc -o prog a.cpp b.cpp alexin@home:~/testing$ ls -l total 72 -rw-r--r-1 alexin inf2000 okt 12 17:11 a.cpp -rw-r--r-1 alexin inf2000 okt 12 17:13 b.cpp -rwxr-xr-x 1 alexin inf2000 okt 26 16:58 prog alexin@home:~/testing$ működik fordítva is: cl b.o a.cpp b.) Tárgymodul és forrásfájl felhasználásával: alexin@home:~/testing$ gcc -o prog a.o alexin@home:~/testing$ ls -l total 72 -rw-r--r-1 alexin inf2000 okt 12 -rw-r--r-1 alexin inf2000 okt 26 -rw-r--r-1 alexin inf2000 okt 12 -rwxr-xr-x 1 alexin inf2000 okt 26 alexin@home:~/testing$

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

b.cpp

17:11 16:58 17:13 16:58

a.cpp a.o b.cpp prog

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

47/114

A tárgymodul-könyvtár készítő program neve: ar c.) Tárgymodulkönyvtár és forrásfájl felhasználásával: alexin@home:~/testing$ ar -cr liba.a a.o alexin@home:~/testing$ gcc -o prog b.cpp liba.a alexin@home:~/testing$ ls -l Fordítás total 72 -rw-r--r-1 alexin inf2000 okt 12 17:11 a.cpp -rw-r--r-1 alexin inf2000 okt 26 16:58 a.o -rw-r--r-1 alexin inf2000 okt 12 17:13 b.cpp -rw-r--r-1 alexin inf2000 okt 26 17:03 liba.a -rwxr-xr-x 1 alexin inf2000 okt 26 17:03 prog alexin@home:~/testing$ A –c jelentése: csak fordítás, szerkesztés nem. d.) Csak tárgymodulok felhasználásával: alexin@home:~/testing$ gcc -c a.cpp b.cpp alexin@home:~/testing$ ld -o prog a.o b.o alexin@home:~/testing$ ls -l total 72 -rw-r--r-1 alexin inf2000 okt 12 17:11 -rw-r--r-1 alexin inf2000 okt 26 17:06 -rw-r--r-1 alexin inf2000 okt 12 17:13 -rw-r--r-1 alexin inf2000 okt 26 17:06 -rwxr-xr-x 1 alexin inf2000 okt 26 17:06 alexin@home:~/testing$

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

A szerkesztő program neve: ld a.cpp a.o b.cpp b.o prog

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

48/114

// // Stack.h header fájl // class Stack definíciója #ifndef #define

STACK_H STACK_H

class Stack { private: int m_aStack[MAX_SIZE] ; int m_iTop ; public: void Push(int i) ; int Pop() ; int Top() { return m_aStack[m_iTop] ; } } ; #endif //#ifndef STACK_H

// // Stack.cpp forrás fájl // class Stack implementációja #include ”Stack.h” void Stack::Push(int i) { m_aStack[--m_iTop] = i ; } int Stack::Pop() { return m_aStack[m_iTop++] ; } A Stack::Pop eljárás törzse kapcsolódik a Stack class scope-hoz, és a benne levő programobjektumok (class memberek) láthatóvá válnak az eljárás törzsében.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

49/114

VIII. Az öröklődés (inheritence) Az egyes programozási nyelvekben a korlátozva lehet az ős osztályok száma. A Delphi Java és Visual Basic nyelvekben egyszeres öröklődés van (single inheritence), míg a C++ nyelvben megengedett több ős osztály alkalmazása (multiple inherintence). Egy struct lehet ős vagy gyerek osztály, míg egy union (vagy enum) nem. Szemben a beágyazással, öröklődés esetén a gyermek osztály az ős osztály egy valódi egybefüggő része lesz. A gyermek osztályhoz tartozó objektumot bármikor kezelhetem úgy mintha az egy ős osztálybeli lenne. Ez működik pointerek esetében is, azaz egy gyermek osztály pointert bármikor legálisan konvertálhatok át ős osztályra mutató pointerré. Az öröklődés során lehetőség van az ős osztály tagjainak láthatósági opcióján változtatni. Ezt az ős osztályok felsorolásakor kell definiálni. Az változtatás csak szigorítást (korlátozást) jelenthet. Az alábbi táblázat a gyermek osztálybeli láthatóságot mutatja be az ős osztálybeli láthatóság és a módosítás függvényében: Az ős osztályok felsorolásakor adott paraméter

A membernek az ős osztályban meglévő láthatósága private

protected

public

private

private

private

private

protected

private

protected

protected

public

private

protected

public

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

50/114

Például: class A : public B, protected C, private D { int m_iTop ; int m_aStack[20] ; ... } ;

Hogyan módosíthatom a metóduskokat a gyermek osztályban? • Az öröklődési hierachiában korábban definiált tetszőleges (protected vagy public) eljárást lehet használni • Az öröklődési hierarchiában előforduló tetszőleges (protected vagy public) member változót lehet használni • Az adott gyermek osztályban definiált új member változókat és eljárásokat is lehet használni • A programban definiált globális változókat illetve az adott metódusban definált lokális változókat lehet használni

Hívatkozás az öröklődési hierarchiában fejlebb (az ős osztályok felé) lévő memberekre. BasicClass::m_adat, Vagy BasicBasicBasicClass::DoSomething() void {

ChildClass::OverloadedProc() if (m_type == 0) BasicClass::OverloadedProc() ; else { // a gyermek osztálybeli bővítés }

}

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

51/114

A viruális öröklődés (virtual inheritence) A gyakorlati programozás során (csak többszörös öröklődésnél) előfordulhat olyan eset, amikor egy-egy ős osztály az öröklődési hierarchia különböző pontján ismét megjelenik. Ekkor a gyermek osztályban ennek az ős osztálynak több példánya jelen(het) meg. Erre néhány esetben nincs szükség, például ha az ős osztály csak egy eljárás-erőforrás, akkor minden esetben elegendő egyetlen előfordulás a gyermek osztályokban.

A virtuális (legfeljebb 1 példányban megjelenő) ős osztályt az őröklődésnél az ős osztályok felsorolásakor adott virtual módosítóval kell jelezni. Például: class B : public virtual A, protected X { ... } ; class C : public virtual A, private Y { ... } ; class D : B, C { ... } ; Ha nem adom meg a virtual módosító szót, akkor az A osztály többször fog megjelenni a D osztály példányaiban. Hivatkozásnál mindig meg kell mondani, hogy az A melyik példányáról van szó: C::A::m_iN, B::A::m_iN.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

52/114

IX. Az objektumok életciklusa (létrehozás és megszüntetés), konstruktorok és a destruktor A C++ programozási nyelvben objektumokat többféle módon is létrehozhatok. Az objektum példányok élettartam, érvényességi kör szempontjából a C/C++ (kb. struct) változóknak felelnek meg, kezelésük, definiálásuk is hasonlóan történik. Egy osztály különböző példányai azonos hosszúságú memória területet foglalnak el (sizeof(ClassA)), ezt le is lehet foglalni: akár az adatszegmensben (sztatikus), a veremben (lokális) vagy a heapben (dinamikus) is. Az objektum példányok átadása eljárás paraméterként ugyanúgy történik mintha egy rekordot adnánk át; a megfelelő hosszúságú bájt sorozat lemásolódik a verembe. Objektum példányok közötti értékadás pedig, ha másképpen nem rendelkezünk, akkor adott számú bájt átmásolását jelenti (memcpy). Az objektum példányok működését szokás egyszerű reguláris automatával modellezni. Ennek az automatának az állapottere, az adat tagok értékkészleteinek direkt szorzata lenne: D(m_v1) × D(m_v2) × ... × D(m_vn). Az állapotátmeneti függvény pedig a metódusokból lenne összeállítható. Állapotváltozás egy metódus meghívásakor következhet be. Ez a modell megengedi az aszinkron eljárás hívást, ami a grafikus felhasználói felületű operációs rendszerek esetében már a gyakorlatban is alkalmazott technika. Az objektum példányok egy várakozási sorban gyűjtik a feladatokat (eljárás hívásaikat), majd alkalmas időben végrehajtják őket. Fontos, hogy a feladatok sorrendje megőrződjön; ezt angolul: message passing-nak nevezik.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

53/114

A konstruktorok A konstruktor az objektumok adat tagjainak kezdeti értékekkel feltöltését végző kitüntetett eljárás. Egy osztályhoz több különböző paraméter listájú konstruktort is lehet készíteni, amelyek közül a megfelelőt a fordító program választja ki. A konstruktor eljárásnak a neve meg kell egyezzen az osztály nevével és visszaadott értéke nem lehet. Az objektum létrehozását a konstruktor már nem akadályozhatja meg. (Vannak olyan implementációk, ahol a konstruktor exception-t (kb. kizárás) tud generálni, az azt kezelő eljárás pedig megakadályozhatja az objektum létrejöttét, vagy a már létrejött objektumot törölheti.) A konstruálhatóság ellenőrzése a konstruktor meghívása előtt kell történjen. Ha a konstruálásban van olyan tevékenység, ami nem feltétlenül ér véget sikeresen, akkor azt ki kell venni a konstruktorból és külön eljárásként a konstruálás után közvetlenül végrehajtani. A paraméter nélküli konstruktor eljárás neve: alapértelmezett (default) konstruktor. Csak ős osztályokban kötelező, akkor ha az osztályból gyermek osztályokat szeretnének létrehozni öröklődéssel. Megvalósítható olymódon is, hogy egy nem default konstruktor minden paraméteréhez default eljárás paramétereket adunk. Amennyiben egy gyermek osztály konstruálunk, akkor a konstruktor minden esetben meg kell hívja rekurzívan az ős osztály(ok) konstruktorait mielőtt elkezdené a saját eljárás törzsét végrehajtani. A konstruktor mindig public (esetleg protected), de semmi esetre sem private. class Fraction { private: number m_szamlalo ; number m_nevezo ; public: Fraction(number = 0, number = 1) ; Fraction(char *) ; } ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

54/114

a.) sztatikus objektum létrehozása az adatszegmensben Fraction a, b, c(3), D(5,2), e(”12/65”) ; b.) lokális objektum létrehozása a veremben void Add(Fraction f1, Fraction f2 = Fraction(”0/1”)) { Fraction res(0), d[2]={Fraction(3,4),Fraction(5,6)}; ... } A fenti Add eljárásban szereplő Fraction(0,1) egy úgynevezett objektum konstans. Objektumokat tartalmazó kifejezésekben vagy default paraméterek esetén használhatjuk. Objektum példányokból további összetett adatstruktúra építhető fel: például tömb, mátrix, létrehozható objektum példányra mutató pointer, vagy objektum példány értékű függvény. Fraction f = 32 * Fraction(6,8) + Fraction(9,15) ; Fraction f[5,5], *pFr, Add(Fraction &, Fraction &) ; c.) dinamikus objektum példány létrehozása a heapben Heapbeli objektumok létrehozása a new operátorral történik, megszüntetésük pedig a delete operátorral. A létrehozáshoz nem elegendő a memória megfelelő méretben történő lefoglalása, hanem a konstruktor eljárást is meg kell hívni. (Ezért nem lehet objektum példányt létrehozni malloc eljárással.) A new operátorral egyetlen objektum példányt vagy megadott méretű tömböt hozhatunk létre. A new operátor alkalmazásának eredménye mindig egy pointer a new operandusában megadott osztályra.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

55/114

Szintaxis: new operátor ::> [ :: ] new type [ inicializálás ] new operátor ::> [ :: ] new type [ tömb_indexek ]

delete operátor ::> delete pointer delete operátor ::> delete [ ] pointer Fraction *p1 = new Fraction ; ... delete p1 ; Fraction *p2 = new Fraction(5,6) ; ... delete p2 ; Fraction *aF = new Fraction[30] ; ... delete [] aF ; A new operátorral a beépített típusok is használhatók pl. char, int, ... double *p3 = new double(32.6) ; ... delete p3 ; double *p4 = new double[30] ; ... delete [] p4 ; Tömbök foglalásakor a default konstruktor hívódik meg. Megszüntetésüknél az üres [] zárójelpár használata kötelező. Implementáció: Egy adott méretnél kisebb memória területet nem lehet foglalni. A kívánt méretet a rendszer mindig egy adott granulációnak megfelelően kerekíti. A foglalásokat egy közös pool területen gyűjti a rendszer, ezért a kisebb pointer hibák tönkre tehetik az egész heapet. Nagyobb foglalásokat célszerű külön szegmensként lefoglalni, de ezt eljáráshívásokkal kell elvégezni a nem pedig a new operátor alkalmazásával. Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

56/114

Az ős osztály konstruktorának kézi hívása Ha több lehetséges konstruktora van az ős osztálynak és azokat egy nem default értékkel kell meghívni, akkor programmal kell biztosítani a paraméterek helyes értékét. class Point { public: Point(int x = 0, int y = 0) ; ... } ; class LabelAtPoint : public Point { private: char m_szLabel[144] ; public: LabelAtPoint(int x = 0,int y = 0,char *text = ””) ; ... } ; LabelAtPoint::LabelAtPoint(int x, int y, char *text) : Point(x, y) { strcpy(m_szLabel, text) ; } Ha több konstruktort kell meghívni, akkor a konstruktor hívásokat vesszővel elválasztva kell felsorolni: LabelAtPoint::LabelAtPoint(int x, int y, char *text) : Point(x, y), Color(Color::red), Rect(x, x+20, y, y+30) { ... }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

57/114

A fenti módon nemcsak az ős osztály konstruktora hívható meg, hanem a lehetőség van egyes adat tagok inicializálására is. (Ez a konstruktor eljárásban is megtehető lenne.) Főként akkor használható, ha az inicializálni kívánt tagok maguk is objektumok, amelyeket nem a default konstruktorral szeretnénk inicializálni: // class Point class Point { public: Point(int x, int y) { m_ix = x; m_iy = y; } private: int m_ix, m_iy ; } ; // A Rect osztály tartalmaz két Point objektumot class Rect { public: Rect(int x1, int y1, int x2, int y2) ; private: Point m_Topleft, m_Bottomright; }; // Konstructor a class Rect számára // expliciten initializálja Point objektumokat Rect::Rect(int x1, int y1, int x2, int y2) : m_Topleft(x1, y1), m_Bottomright(x2, y2) { }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

58/114

A destruktor Az objektumok megszüntetése előtti „takarítás” – erőforrás felszabadítás a feladata. A neve meg kell egyezzen az osztály nevével, ami elé egy ~ (tilde) jelet is kell tenni (amit nem feltétlenül kell egybe írni a névvel). Paramétere és visszaadott értéke nem lehet. A destruktor már nem állíthatja meg az objektum megszüntetését. Amikor a destruktor véget ér, az objektumot a rendszer a memóriából törli. Mindig a gyerek osztály destruktora hívódik meg először, és azt követi rekurzívan az ős osztályok destruktorainak a meghívása: #include class String { public: String(char *ch) ; ~ String() ; private: char *m_szText ; } ;

// Konstruktor // és destruktor

String::String(char *ch) { // dinamikus helyfoglalás. m_szText = new char[strlen(ch) + 1] ; // ha a helyfoglalás sikeres, akkor másolás if(m_szText != NULL) strcpy(m_szText, ch) ; } String::~ String() { // A dinamikusan lefoglalt memória felszabadítása delete [] m_szText ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

59/114

Mikor van szükség konstruktor és destruktor alkalmazására? A C/C++ programok az operációs rendszertől erőforrásokat kérhetnek, amelyeket bizonyos ideig használnak, majd – ha már nincs rá szükségük – felszabadítanak. Egy jól megírt C++ programban az erőforrásoknak osztályokat feleltetünk meg. Ilyen erőforrások például a: • • • • • • •

fájlok ablakok memória területek hálózati kapcsolatok nyomtatók floppy (CD) egység hang periféria

Amikor a programnak szüksége van egy erőforrásra, akkor a megfelelő számú objektum példányt létrehozza az erőforráshoz tartozó osztályból. A helyes programozási stílus az, ha: • a konstruktor eljárás foglalja le a szükséges erőforrást • a destruktor eljárás szabadítja fel a szükséges erőforrást Abban az esetben, ha az erőforrás lefoglalása bonyolúltabb tárgyalás eredménye, akkor ehelyett a következő megoldás ajánlott: • a konstruktor eljárás csak kezdőértékekkel töltse fel az objektum példányt • egy BOOL Init(); és egy BOOL IsAlreadyInitialized(); eljárást célszerű írni. Az Init() eljárást addíg kell hívni, amíg végül az erőforrást sikeresen le lehet foglalni. A későbbiekben pedig mindig lehet ellenőrizni (IsAlreadyInitialized()) hogy az erőforrás valóban rendelkezésre áll-e?

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

60/114

#include class proba { private: int x, y ; public: proba(int = 0, int = 0) ; ~proba() ; } ; proba::proba(int a, int b) { x = a ; y = b ; printf("Egy proba(%d,%d) objektum létrehozva.\n",x,y); } proba::~proba() { printf("Egy proba(%d,%d) objektum törölve.\n",x,y) ; } static proba p1(2,3) ; void main() { printf("A main törzsében ...\n") ; proba c(3,4), d, h ; proba *e = new proba(6,7) ; proba *f = new proba[3] ; delete [] f ; proba *g = new proba[4] ; delete [] g ; delete e ;

Legelőször a static objektum jön létre, még a main() előtt. A c objektum paraméterekkel, a d és a h objektum a default konstruktorral jön létre. Tömb esetén delete [] kell! Ha hiányzik, akkor: Halom sérült!– hibaüzenet.

printf("A main törzsét elhagyva ...\n") ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

61/114

A fenti program outputja: Egy proba(2,3) objektum A main törzsében ... Egy proba(3,4) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(6,7) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(6,7) objektum A main törzsét elhagyva Egy proba(0,0) objektum Egy proba(0,0) objektum Egy proba(3,4) objektum Egy proba(2,3) objektum

létrehozva.

p1

létrehozva. létrehozva. létrehozva. létrehozva. létrehozva. létrehozva. létrehozva. törölve. törölve. törölve. létrehozva. létrehozva. létrehozva. létrehozva. törölve. törölve. törölve. törölve. törölve. ... törölve. törölve. törölve. törölve.

c

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

d és h e f f törlése

g

g törlése e törlése d és h c p1

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

62/114

// alloc.cpp : GB memória foglalás // #include typedef double LD_array[2000000] ; // 16MB méretű tömb typedef double *D_Ptr ; D_Ptr MTable[512] ; // max. 512 x 16MB = 8GB void mem1G(int i) { // 8 x 16MB = 128 MB lefoglalása for(int k = 0 ; k < 8 ; ++k) { int idx = (i > operátorral az stdin fájlból. Kaszkádolható.

void main() { int a, b ; cin >> a >> b ; cerr Drawit() hívás a szülő osztálybeli Drawit() metódust hívná meg, míg késői hozzárendelés esetén a megfelelő gyermek osztálybeli metódus kerül meghívásra (mert a VFT táblázatban ennek az eljárásnak a címe van).

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

87/114

Virtuális eljárások használata Ha egy eljárást virtuális eljárásként kívánunk megírni, akkor a class utasításban az eljárás prototípusa elé a virtual módosító szót (modifier) ki kell tenni. A későbbiekben a gyermek osztályokban előforduló ugyanilyen szignatúrájú (paraméterlistájú) eljárások mind virtuálisak lesznek. Azonban ezt nem szükséges jelezni. (Visual Studio 2010-ben elég a virtual kulcsszót az ősosztályban kiírni.)

Újrafelhasználhatóság és a virtuális eljárások Az újrafelhasználás során nagy valószínűséggel módosításra kerülő eljárásokat a szülő osztályokban célszerű egyből virtuálisra megírni, mert ezzel jelentős munkát lehet megtakarítani a későbbiekben (különösen akkor, ha a szülő osztály forráskódját nem akarjuk továbbadni, így abban újabb virtuális eljárást nem lehet utólag felvenni). Van olyan programozási nyelv (Java), amelyben már eleve csak virtuális eljárások vannak. class FTPClient { public: void Open() ; void Close() ; void Encode() ; void SendFile(char *) ; } ; void FTPClient::Open() { hChannel = openChannel(25) ; } void FTPClient::Close() { closeChannel(hChannel) ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

88/114

void FTPClient::SendFile(char *filename) { Open() ; ... Close() ; } Ha adva van a fenti FTPClient osztály és szeretnénk egy új SecureFTPClient osztályt ebből számraztatni, akkor hogyan valósíthatjuk meg a legkevesebb programsor megírásával? class SecureFTPClient : public FTPClient { ... } ; Nyilvánvalóan az lenne a legjobb, ha az Open(), a Close() és az Encode() kivételével minden megmaradna. Azonban, ha a korai hozzárendelést használjuk, akkor a SendFile() eljárásból hívott Open() és Close() eljárások címe fixen be lenne égetve (a szülő osztálybeli SendFile() eljárás címére). Ahhoz, hogy ez ne így legyen, az Open() és Close() eljárásokat virtuális eljárásokká kell alakítani. class FTPClient { public: virtual void Open() ; virtual void Close() ; virtual void Encode() ; void SendFile(char *) ; } ; Ugyanez érvényes az Encode() eljárásra is, amennyiben a SendFile() eljárás használja. Továbbá magának a SendFile() eljárásnak is megfontolandó a virtual modifier megadása.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

89/114

Pure virtual eljárások és az absztrakt osztályok A törzs nélküli virtuális eljárásokat pure virtual eljárásoknak nevezzük. A pure virtual eljárás egy üres (NULL) bejegyzést foglal el a VFT táblázatban. Ha egy osztály ilyen eljárást tartalmaz, akkor azt absztrakt osztálynak nevezzük amiatt, mert ebből az osztályból objektum példányokat létrehozni nem lehet. A gyermek osztályokban minden pure virtual eljárást megfelelő törzzsel kell ellátni, ezt a fordító ellenőrzi. Amíg egyetlen pure virtual eljárás is marad, az osztály absztrakt lesz. A pure virtual eljárás jelölése: virtual void Drawit() = 0 ; Az eljárástörzs helyén egy =0 áll. A szintaxisban ”=” szerepel, azonban a nem 0 érték szemantikus hibát okoz.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

90/114

A következő példaprogram bemutatja a virtual eljárások működését. A négyszög ősosztályból gyermekosztályokat származtathatunk (négyzet, téglalap). Egy Drawit() eljárás a rajzolná ki a példány típusának megfelelő objektumot a képernyőre. Az objektumokat egy QuadrilateralList listában tároljuk, és a közös ősosztály pQuadrilateral pointeren keresztül hívjuk meg a Drawit() eljárást. Quadrilateral

Square

Rectangle …

Meghíváskor akkor tud a megfelelő eljárás aktiválódni, ha az objektumpéldányok megőrzik a létrehozáskori típusukat. Mivel a példányok címét tartalmazó pointerek (referenciák) típusát a program futása során igény szerint konvertálhatjuk ős vagy gyerekosztály típusúra, ezért meg kell különböztetnünk az ún. static és a dynamic típusokat egymástól. A static type a forráskód alapján, a változók típusából kikövetkeztethető típus. Ennek nem sok köze van a létrehozáskori típushoz, attól lényegesen eltérhet. Azonban a fordító program alapvetően ebből tud kiindulni, a kódot futtatás előtt, a konkrét futási adatok nélkül kell lefordítania. Ha a forráskódban valami CType * típussal szerepel, akkor a fordító automatikusan a CType osztály egy példányára mutató pointerként fogja kezelni. A dynamic type (típus) akkor határozható meg, ha az objektum példányok tárolják és megőrzik a létrehozáskor kapott típusukat. Amennyiben a dynamic type alapján (virtuális függvénytábla lényegében = dynamic type) működő kódot képes a fordító előállítani, akkor a program intelligensebb módon, a mindenkori létrehozási típus alapján, futás közben, a meghívás előtt határozza meg a meghívandó virtuális eljárás címét.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

91/114

#include A Quadrilateral ősosztály class Quadrilateral { public: int NonVirtual() { return 1 ; } virtual void Drawit() = 0 ; } ; A Square gyermekosztály virtual Drawit() metódussal

class Square : public Quadrilateral { public: int NonVirtual() { return 2 ; } virtual void Drawit() { printf("Square.\n") ; } } ; class Rectangle : public Quadrilateral { public: int NonVirtual() { return 3 ; } virtual void Drawit() { printf("Rectangle.\n") ; } } ;

A Rectangle gyermekosztály virtual Drawit() metódussal Láncolt lista négyszögek csoportjának tárolására

typedef struct QuadrilateralListTag { Quadrilateral *m_pQuadrilateral ; QuadrilateralListTag *m_pNextQuadrilateral ; } QuadrilateralList ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

92/114

Kételemű lista egy téglalap és egy négyzet.

int main() { QuadrilateralList *pBeginQuadrilateralList = QuadrilateralList ; pBeginQuadrilateralList->m_pQuadrilateral = Rectangle ;

new new

QuadrilateralList *p = new QuadrilateralList ; pBeginQuadrilateralList->m_pNextQuadrilateral = p ; p->m_pQuadrilateral = new Square ; p->m_pNextQuadrilateral = NULL ;

}

QuadrilateralList *pList ; A lista bejárása pList = pBeginQuadrilateralList ; while ciklussal while (pList != NULL) { pList->m_pQuadrilateral->Drawit() ; pList = pList->m_pNextQuadrilateral ; } Az m_pQuadrilateral pointer static típusa return 0 ; Quadrilateral, míg a dynamic típusa Rectangle vagy Square.

C:>cl j.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler j.cpp C:>j Rectangle. Square.

Az objektumok „emlékeznek” a létrehozáskori típusukra.

C:>

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

93/114

PUBLIC _main EXTRN ??2@YAPAXI@Z:PROC ; operator new ; Function compile flags: /Odtp A main() eljárás ; File c:\j.cpp kezdete. _TEXT SEGMENT _pList$ = -12 ; size = 4 _pBeginQuadrilateralList$ = -8 ; size = 4 _p$ = -4 ; size = 4 _main PROC ; 36 : { 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 83 ec 24 sub esp, 36 ; 00000024H Indirekt metódus hívás az eax regiszteren keresztül. ; 48

:

while (pList != NULL)

000a3 83 7d f4 00 cmp DWORD PTR _pList$[ebp], 0 000a7 74 1b je SHORT $LN1@main ecx = az adott ; 49 : { ; 50 : pList->m_pQuadrilateral->Drawit() ; listaelemnél a this 000a9 8b 4d f4 mov ecx, DWORD PTR _pList$[ebp] edx = virt. fgv table címe 000ac 8b 11 mov edx, DWORD PTR [ecx] 000ae 8b 45 f4 mov eax, DWORD PTR _pList$[ebp] eax = első 000b1 8b 08 mov ecx, DWORD PTR [eax] metódus címe 000b3 8b 12 mov edx, DWORD PTR [edx] 000b5 8b 02 mov eax, DWORD PTR [edx] 000b7 ff d0 call eax ; 51

:

pList = pList->m_pNextQuadrilateral ;

000b9 8b 4d f4 mov ecx, DWORD PTR _pList$[ebp] 000bc 8b 51 04 mov edx, DWORD PTR [ecx+4] 000bf 89 55 f4 mov DWORD PTR _pList$[ebp], edx ; 52 : }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

94/114

000c2 eb df jmp SHORT $LN2@main $LN1@main: ; 53 : ; 54 : return 0 ; 000c4 33 c0 xor eax, eax ; 55 : } A Rectangle osztály 000c6 8b e5 mov esp, ebp virtuális függvénytáblája. 000c8 5d pop ebp A vft. osztály szintű 000c9 c3 ret 0 tulajdonság, elég _main ENDP osztályonként egy. _TEXT ENDS PUBLIC ??_7Rectangle@@6B@ ; Rectangle::`vftable' CONST SEGMENT ??_7Rectangle@@6B@ DD FLAT:??_R4Rectangle@@6B@ ; Rectangle::`vftable' A Rectangle DD FLAT:?Drawit@Rectangle@@UAEXXZ osztály Drawit() ; Function compile flags: /Odtp metódusának címe. CONST ENDS Generált konstruktor a _TEXT SEGMENT Rectangle osztályhoz. _this$ = -4 ; size = 4 ??0Rectangle@@QAE@XZ PROC ; Rectangle::Rectangle, COMDAT ; _this$ = ecx A 0 című helyre 00000 55 push ebp bemásolja a vft. címét. 00001 8b ec mov ebp, esp 00003 51 push ecx 00004 89 4d fc mov DWORD PTR _this$[ebp], ecx 00007 8b 4d fc mov ecx, DWORD PTR _this$[ebp] 0000a e8 00 00 00 00 call ??0Quadrilateral@@QAE@XZ 0000f 8b 45 fc mov eax, DWORD PTR _this$[ebp] 00012 c7 00 00 00 00 00 mov DWORD PTR [eax], OFFSET ??_7Rectangle@@6B@ 00018 8b 45 fc mov eax, DWORD PTR _this$[ebp] 0001b 8b e5 mov esp, ebp 0001d 5d pop ebp 0001e c3 ret 0 ??0Rectangle@@QAE@XZ ENDP ; Rectangle::Rectangle _TEXT ENDS

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

95/114

PUBLIC ??_7Square@@6B@ ; Square::`vftable' CONST SEGMENT ??_7Square@@6B@ DD FLAT:??_R4Square@@6B@ ; Square::`vftable' A Square osztály DD FLAT:?Drawit@Square@@UAEXXZ Drawit() ; Function compile flags: /Odtp metódusának címe. CONST ENDS _TEXT SEGMENT _this$ = -4 ; size = 4 ?Drawit@Square@@UAEXXZ PROC ; Square::Drawit, COMDAT ; _this$ = ecx A Square osztály ; 16 : { printf("Square.\n") ; } Drawit() eljárása. 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 51 push ecx 00004 89 4d fc mov DWORD PTR _this$[ebp], ecx 00007 68 00 00 00 00 push OFFSET ??_C@_08IPNBDGB@Square?4?6?$AA@ 0000c e8 00 00 00 00 call _printf 00011 83 c4 04 add esp, 4 00014 8b e5 mov esp, ebp 00016 5d pop ebp 00017 c3 ret 0 ?Drawit@Square@@UAEXXZ ENDP ; Square::Drawit _TEXT ENDS

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

96/114

_TEXT SEGMENT _this$ = -4 ; size = 4 A Quadrilateral ??0Quadrilateral@@QAE@XZ PROC ; osztály virtuális Quadrilateral::Quadrilateral, COMDAT függvénytáblája. ; _this$ = ecx A vft. osztály szintű 00000 55 push ebp tulajdonság, elég 00001 8b ec mov ebp, esp osztályonként egy. 00003 51 push ecx 00004 89 4d fc mov DWORD PTR _this$[ebp], ecx 00007 8b 45 fc mov eax, DWORD PTR _this$[ebp] 0000a c7 00 00 00 00 00 mov DWORD PTR [eax], OFFSET ??_7Quadrilateral@@6B@ 00010 8b 45 fc mov eax, DWORD PTR _this$[ebp] 00013 8b e5 mov esp, ebp 00015 5d pop ebp 00016 c3 ret 0 ??0Quadrilateral@@QAE@XZ ENDP ; Quadrilateral::Quadrilateral _TEXT ENDS

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

97/114

XII. A static memberváltozók és metódusok A static tagváltozók A C++ programozási nyelvben az osztály membereire alkalmazott static módosító (modifier) jelentése eltér a C nyelvben megismerttől. Adattagok esetén a static jelentése az, hogy az illető member változó osztályonként egy példányban kerül lefoglalásra. Ilyenkor a member változó számára az adat szegmensben foglalódik hely egy példányban. A későbbiekben minden objektum példány ezt az egy (közös) területet fogja használni. Fontos tudni, hogy ez nem alkalmas olyan helyzetek megoldására, amikor azonos objektumok egyes csoportjai használnak közös adatterületet, de egy programon belül több csoport lehet pl. lista kezdete, fa gyökere stb. Abból a szempontból, hogy a tárgymodulba bekerül-e ez a member változó a következő igaz: a static memberek tárolási osztálya extern. Magyarul a korábban megismertek – miszerint a static módosító hatására az adott azonosító nem kerül be a tárgymodulba – ebben az esetben nem igazak. class Dialog { private: static int sm_iFontType ; static int sm_iTitleFontType ; public: Dialog() ; } ; int Dialog::sm_iFontType = ANSI_VAR_FONT ; int Dialog::sm_iTitleFontType = sm_iFontType ; A példából látható, hogy a static adattag inicializálása: a.) a global scope-ban történik meg (nem a konstruktorban). Ilyenkor a konstruktorban már látható a beállított érték. Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

98/114

b.) a kezdőérték-adás elmaradása szerkesztéskor fellépő hiba c.) az inicializálás a Dialog class scope-ban megy végbe, ezért a jobb oldalon minden más static adattag illetve static metódus hívható.

A static metódusok A C++ programozási nyelvben az osztályokban lehetnek static metódusok. A metódusok esetén ez a következőt jelenti: a metódus a class scope-jában van, ezért mindent, ami ott található használhat (nested típusokat, enum konstansokat, static adattagokat, static metódusokat), szemben a friend eljárással, amely nem lesz az osztály scope-ban. A static eljárás nem rendelkezik azzal az implicit pointer paraméterrel (this), ami egy aktuális objektumra mutat. A static metódus nem lehet virtuális. Nem hívható meg oBj.StProc(), vagy pObj->StProc() módon (pontosabban ilyenkor az elöl álló objektum változót (pointert) a rendszer figyelmen kívül hagyja). Ez akkor fontos, ha pObjFunc()->StProc() –ként akarjuk meghívni a static metódust, mert ekkor a pObjFunc() függvény meghívása elmarad. class Dialog { private: static int sm_count ; static int sm_iFontType ; static int sm_iTitleFontType ; public: Dialog() ; static int Count() ; } ; int Dialog::Count() { return sm_count ; } A hívása bárhonnan lehetséges: Dialog::Count()-ként.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

99/114

Visszahívható (callback) függvények 1. A privilegizált operációs rendszerek működésének alapelvei: •

• •

A gépi utasításokat csoportosították és az operációs rendszer számára a teljes utasításkészletet biztosították, míg a felhasználói programok számára egy csökkentett – kevésbe veszélyes készlet áll rendelkezésre. Négy privilégium szint: 0 – kernel; 1, 2; 3 – felhasználói program, Windowsban csak a 0 és a 3 használt. A különböző szinteken futó programok egymással csak a regisztereken át tartanak kapcsolatot, teljesen különálló adat és verem szegmenst használnak. A kernel beleláthat a felhasználói szegmensekbe, a felhasználói program viszont nem láthat bele a kernel szegmenseibe.

Hogyan tarthat fenn kapcsolatot egymással a felhasználó és a kernel? •



Megszakításkérések segítségével (régen SVC – supervisor call, ma inkább INT – interruptnak hívják). A CPU egy területén 256 megszakításkezelő eljárás címe van, ezek közül az egyiket, ha ismerem a számát, meghívhatok. A CPU regisztereivel (AL, AX), tovább finomíthatom a kérésem és elágazhatok további kiszolgáló eljárásokhoz. A Windowsban az INT 0x2F megszakítás és az AX regiszter együtt, 5-10 ezer kiszolgáló eljárást tesz hozzáférhetővé. A kernel által visszahívható (callback) eljárások. A felhasználó által írt, előre megadott paramétereket fogadó speciális eljárások címét a kernel számára átadhatjuk, amely azokat ha szükséges meghívja. Elterjedten használják ablakok üzeneteinek feldolgozó eljárásainak megvalósítására. Az ablak létrehozásakor a kezelő eljárás egy alapértelmezés szerinti eljárás, amely minden üzenetet átenged. Ezt lehet a felhasználónak helyettesítenie egy saját eljárással.

2. A callback eljárások tulajdonságai •

extern ”C” eljárások kell legyenek, hogy ne csak C++ programok használhassák a szolgáltatást. Ezért viszont nem lehetnek közönséges tageljárások egy osztályban.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

100/114

extern ”C” eljárás is lehet tagja egy osztálynak, ha nincs szüksége az aktuális objektumra, tehát osztály-szintű eljárás, azaz static tageljárás. A static tageljárások legfontosabb felhasználási területe az, hogy olyan eljárásokat lehessen készíteni velük, amelyek tagjai egy adott ablak osztálynak, elérik az osztály attribútumait és eljárásait, ezen kívül azonban hagyományos C eljárások, amelyeket a kernel vissza tud hívni. Az erőforrások azonosítása hogyan történik? A kernel minden grafikus elemet egy egyértelmű azonosító számmal lát el (HANDLE típusú), ezzel lehet a későbbiekben rá hivatkozni. A felhasználói program objektumai is kell, hogy ezt az azonosítószámot tárolják. A callback függvény az aktuális paraméterei között megkapja ezt az azonosító számot, ebből tudja, hogy az üzenet, melyik grafikus elemnek érkezett, ezután sorban végigjárja a memóriában lévő objektumokat és megkeresi azt, amelyikben a tárolt azonosító szám megegyezik a kapottal. Ha megvan a memóriabeli párja a kernel objektumnak, akkor már az adott objektum példány adataival együtt lehet kezelni az érkezett üzenetet.

BOOL CALLBACK { DWORD DWORD TERMINFO

TerminateAppEn(HWND hwnd, LPARAM lParam) dwID ; dwThread ; *termInfo ;

termInfo = (TERMINFO *)lParam ; dwThread = GetWindowThreadProcessId(hwnd, &dwID) ; if(dwID == termInfo->dwID && termInfo->dwThread == dwThread ) { PostMessage(hwnd, WM_CLOSE, 0, 0) ; } return TRUE ; }

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

101/114

Hívó program hProc = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwPID); if(hProc == NULL) { return TA_FAILED ; } // TerminateAppEnum() posts WM_CLOSE to // all windows whose PID matches your process's. EnumWindows((WNDENUMPROC)TerminateAppEn, (LPARAM) dwPID) ; // Wait on the handle. If it signals, great. // If it times out, then you kill it. if (WaitForSingleObject(hProc, dwTimeout) != WAIT_OBJECT_0) dwRet = (TerminateProcess(hProc,0) ? TA_SUCCESS_KILL : TA_FAILED); else dwRet = TA_SUCCESS_CLEAN ; CloseHandle(hProc) ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

102/114

XIII. Kivételkezelés A ciklusból történő kiugrásra a break utasítás szolgál, az eljárásból történő azonnali kilépésre a return utasítás. A programok fejlesztése azonban egy ennél bonyolúltabb rendszert tett szükségessé. Definíció: A kivétel a program futása során előálló rendellenes állapot, amely közbeavatkozás nélkül a program futásának abnormális megszakadását eredményezheti. A kivételes helyzetek kezelésének elmulasztása például a hálózati a kommunikáció, vagy az adatbázis tranzakció félbeszakadásával járhat úgy, hogy mindeközben meghatározatlan állapotba kerül a rendszer. A kivételkezelés eszköze C++-ban a try és a catch utasítás, míg a manuális kivétel kiváltásra szolgál a throw utasítás. A kivétel bekövetkezésekor a throwbeli kifejezés típusának megfelelő catch blokk hívódik meg, ezt a veremben visszafelé haladva keresi meg a rendszer. A verem tartalmát az adott pontig kiüríti a rendszer, végrehajtja a catch blokkot, majd a try utáni sorral folytatja a végrehajtást. A try utasítás-blokk szintaxisa: try-blokk ::> try összetett-utasítás kivétel-kezelő-lista kivétel-kezelő-lista ::> kivétel-kezelő + kivétel-kezelő ::> catch ( kivétel-deklaráció ) összetett-utasítás kivétel-deklaráció ::> típus-spec-lista deklarátor | típus-spec-lista absztrakt-deklarátor | típus-spec-lista | … throw-utasítás ::> throw kifejezés

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

103/114

#include char fnct(int a) { int k ; try { k = 43 ; throw (long) k ; } catch (int) { printf("(int) Exception in fnct.\n") ; } printf("last line in fnct.\n") ; return 'a' ; } int main() { int t ; try { t = 12 ; fnct(t) ; } catch (long) { printf("(long) Exception in main.\n") ; } catch (...) { printf("(any) Exception in main.\n") ; } printf("last line in main.\n") ; return 0 ; }

Az eredmény: (long) Exception in main. last line in main.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

104/114

XIV. Függelék: fontosabb ANSI C eljárások és használatuk 1. A main() eljárás paraméterei A C és a C++ programok kezdő eljárása minden esetben a main() eljárás (Windowsban WinMain, de ennek a paraméterezése is más). extern ”C” int main(int argc, char *argv[], char *envp[]) ;

vagy extern ”C” int main(int argc, char **argv, char **envp) ;

ahol • az argc a parancssorban szereplő argumentumok száma, • az argv a string alakban tárolt argumentumok címeit tároló tömb, az első argumentum címe argv[0], a másodiké argv[1], …, az utolsó argumentum után egy NULL pointer következik. • az envp a string alakban tárolt környezeti változók címeit tároló tömb, az első argumentum címe envp[0], a másodiké envp[1], …, az utolsó környezeti változó után egy NULL következik. Amennyiben a C string alakban szereplő paramétereket át kellene alakítani szám típusúra, úgy az sscanf() (stdio.h), a _gcvt() (math.h), atoi(), atof(), (stdlib.h) eljárásokat kell használni. int k ; float z ; sscanf(argv[0], ”%d”, &k) ; sscanf(argv[1], ”%f”, &z) ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

105/114

Példa a main() paramétereinek használatára: #include int main(int argc, char **argv, char **envp) { int i ; char **p ; printf("Az argc: %d.\n", argc) ; i = 0 ; p = argv ; while(*p) printf("A(z) %d-ik arg: %s.\n", i++, *p++) ; i = 0 ; p = envp ; while(*p) printf("A(z) %d-ik env: %s.\n", i++, *p++) ; return 0 ; } E:\VCProjects\p1>pelda "a~\"" "Microsoft Visual Studio" 3 Az argc: 4. A(z) 0-ik arg: a. A(z) 1-ik arg: a~". A(z) 2-ik arg: Microsoft Visual Studio. A(z) 3-ik arg: 3. A(z) 0-ik env: CLIENTNAME=Console. A(z) 1-ik env: ComSpec=C:\WINDOWS\system32\cmd.exe. A(z) 2-ik env: FP_NO_HOST_CHECK=NO. A(z) 3-ik env: HOMEDRIVE=C:. A(z) 4-ik env: PROCESSOR_LEVEL=15. A(z) 5-ik env: PROCESSOR_REVISION=0207. A(z) 6-ik env: ProgramFiles=C:\Program Files. A(z) 7-ik env: PROMPT=$P$G. A(z) 8-ik env: SESSIONNAME=Console. A(z) 9-ik env: SystemDrive=C:.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

106/114

2. A C/C++ prepocesszor utasítások A preprocesszor a C/C++ fordító program működése előtt fut le, és a forrásprogram sorait módosíthatja. A feltételes fordítási direktívák használatával egyes kódrészleteket kihagyhatunk, vagy az include direktívával beilleszthetünk. Minden preprocesszor utasítás (direktíva) egy # jellel kezdődik, amely elválhat az utána következő kulcsszótól. C/C++ és a preprocesszor utasítások nem lehetnek egy közös sorban. A preprocesszorban szereplő kifejezések típusa mindig int, valós kifejezések használata nem megengedett. A helyettesítések pedig szövegesen történnek. # define M # define M for(int i = 0 ; i < 45 ; ++i) #define M(n) for(int i = 0 ; i < n ; ++i) A define utasítás token helyettesítéseket képes előírni (csak akkor helyettesíti M-et, ha az különálló token). A string belsejében szereplő tokeneket a gcc helyettesíti (figyelmezetetés/warning mellett), azonban ez problémát jelenthet. A defined() preprocesszor függvényen keresztül egy feltételes fordítási lehetőséget biztosít. A feltételes fordítási utasítások, (amelyeknek lehet #else ága is) az #endif bezáró sorig terjedő forrássorokat ki tudják venni a #ifdef M #if defined(M) #ifndef M #if !defined(M) #if (defined(UNIX) && defined(GNU)) #else #endif #undef M

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

107/114

A #elif utasítás a #else és a #if –nek felel meg. Az #undef direktíva megszünteti a paraméterként kapott makrót. Az #include direktíva forráskód (ez szinte mindig header vagy template fájl) beillesztésére szolgál. Az #include rendszerint egy vagy több, előre adott alkönyvtárban keres, amely útvonalat egy környezeti változó tartalmazza (pl. INCLUDE=C:\Program Files\Microsoft Visual Studio\vc98\include). Amennyiben rendszer header fájlt szeretnénk beilleszteni, úgy a #include alakot kell használnunk, ha az aktuális alkönyvtárban keresünk, akkor a #include ”my-header.h” alakot. A header fájlok további header fájlokat illeszthetnek be (rekurzív include). Az esetleges végtelen beillesztéseket feltételes fordítással lehet elkerülni. Ehhez az szükséges, hogy minden header fájl tartalma egy feltételes fordítási utasítás belsejében legyen. #ifndef STDIO_H #define STDIO_H ... rekurzív include-ok ... (ide jön a header fájl belseje) #endif Az STDIO_H nevű makró defined lesz, ezért minden további beillesztést meggátol.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

108/114

3. A szövegkezelés eljárásai (string.h) A legfontosabb string kezelő függvények, amelyeket programfejlesztés során lehet használni a következők:

C

és

C++

int strlen(const char *str) ; // string hossza a ’\0’ nélkül char *strcat(char *dest, const char *source) ; // két string egyesítése char *strncat(char *ds, const char *sr, size_t cnt) ; // legfeljebb cnt character másolása a ds string végére char *strcpy(char *dest, const char *source) ; // a source másolása a dest területre int strcmp(const char *str1, const char *str2) ; // két string összehasonlítása ASCII kódjaik alapján // visszatérési érték: // 0 – egyenlők, // 0 (+1) az első string nagyobb int stricmp(const char *str1, const char *str2) ; // két string összehasonlítása ASCII kódjaik alapján // a kis- és nagybetűket nem különbözteti meg int strncmp(const char *s1,const char *s2,size_t cnt) ; // két string első cnt characterének összehasonlítása char *strchr(const char *string, int ch) ; // a ch character első előfordulásának keresése char *strrchr(const char *string, int ch) ; // a ch character utolsó előfordulásának keresése

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

109/114

char *strstr(char *string, const char *str) ; // a str string első előfordulásának keresése char *strlwr(char *string) ; // kisbetűsre alakítás (lower case) char *strupr(char *string) ; // nagybetűsre alakítás (upper case) char *strrev(char *string) ; // string megfordítása char *strset(char *string, int c) ; // egy string minden characterének beállítása c-re char *strnset(char *string, int c, size_t cnt) ; // egy string első cnt characterének beállítása c-re // Figyelem! Nem tesz a characterek után ’\0’-t.

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

110/114

4. A matematikai könyvtár eljárásai (math.h) A legfontosabb matematikai függvények, amelyeket C/C++ programfejlesztés során lehet használni a következők: int abs(int n) ; // abszolút érték long labs(long l) ; // long abszolút érték double fabs(double x) ; // valós abszolút érték double sin(double x) ; // sin(x) értéke double cos(double x) ; // cos(x) értéke double tan(double x) ; // tan(x) értéke - tangens double asin(double x) ; // arcsin(x) értéke – arcus szinusz double acos(double x) ; // arccos(x) értéke – arcus koszinusz double atan(double x) ; // arctan(x) értéke – arcus tangens double sinh(double x) ; // sinh(x) értéke – szinusz hiperbolikusz double cosh(double x) ; // cosh(x) értéke – koszinusz hiperbolikusz double tanh(double x) ; // tanh(x) értéke – tangens hiperbolikusz

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

111/114

double pow(double x, double y) ; // x^y hatvány kiszámítása double sqrt(double x) ; // x négyzetgyöke (square root) double fmod(double x, double y) ; // x/y valós maradéka double exp(double x) ; // e^x hatvány kiszámítása double log(double x) ; // természetes alapú logatimus double log10(double x) ; // 10-es alapú logatimus int srand(unsigned int seed) // véletlenszám generátor inicializálása a seed-del int rand() ; // véletlenszám generátor int atoi(const char *) ; // string átalakítása egész számmá long atol(const char *) ; // string átalakítása long egész számmá double atof(const char *) ; // string átalakítása valós számmá double ceil(double x) ; // felső korlát (legkisebb egész, ami nagyobb x-nél) double floor(double x) ; // alsó korlát (legnagyobb egész, ami kisebb x-nél)

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

112/114

5. Az ANSI C standard stream B/K eljárásai A C-ben a fájlok (stream) azonosítására egy FILE * pointer szolgál. Minden esetben a fájl műveleteknek ezt át kell adni, mert ezzel tudjuk biztosítani, hogy a műveleteket a kívánt fájlon végezzék el. Három előre megnyitott fájl van minden egyes programhoz: stdin, stdout, stderr. Ezeket nem kell megnyitni, már megnyitva kapjuk őket. Amennyiben át kívánjuk őket irányítani pl. egy mágneslemez állományhoz, csupán ekkor van szükség a kezelésükre. Az stdin az ún. standard input fájl, a billentyűzethez kapcsolódik. Az stderr, a standard hiba fájl, a képernyőhöz kapcsolódik (ezt nem szokás átirányítani). Az stdout, a standard output fájl, amely kezdetben a képernyőhöz kapcsolódik, de ezt át lehet irányítani más fájlba. A mágneslemez állományokat a programnak kell megnyitnia, erre az fopen() eljárás szolgál. A fájlokat meg lehet nyitni csak olvasásra, olvasásra és írásra, hozzáfűzésre. A fájlok kezelése történhet binárisan és szöveges módon. A szöveges mód abban különbözik a bináris módtól, hogy itt egyes esetekben a ’\n’ (new line) sorvége charactert a rendszer ”\r\n” (CR+LF) stringgé alakítja [’\r’ (carriage return), ’\n’ (line feed)]. Az MSDOS szöveges fájlok sorainak végén egy két characterből álló sorvége jel (EOLN End Of LiNe) van. A fájlok kezelése általában blokkolt, azaz egy 512 bájt méretű (ekkora a diszk legkisebb egyszerre olvasható egysége, a szektor mérete) pufferben tárolja a rendszer az írni/olvasnivalót, amikor a puffer megtelt, akkor írja ki a diszkre, amikor a puffert elolvasta, akkor olvas be újabb puffert. A pufferek kiürítése a lezárásnál automatikus, azonban ha váltogatunk írás és olvasás között, akkor kézzel kell üríteni a puffereket az fflush() függvény hívásával. A képernyőre írásnál is előfordulhat, hogy egy prompt még nem jelenik meg, mert a kiíratás blokkolt, de az input művelet várakozik az adatra és a felhasználó nem tudja, hogy mit kell tennie. A megoldás az fflush(stdout) hívás. A legfontosabb stream B/K függvények, amelyeket C/C++ programfejlesztés során lehet használni a következők:

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

113/114

FILE *fopen(const char *file, const char *mode) ; // fájlmegnyitás, NULL-ha nem sikerült a megnyitás/hiba // mode: ”r” read only, ha nincs a fájl, akkor hiba // ”r+” read and write, ha nincs meg a fájl, akkor hiba // ”w” write, ha megvan a fájl a tartalma elvész // ”w+” read and write, ha már megvan a tartalma elvész // ”a” append, a CTRL-Z jelet meghagyja // ”a+” append and write, a CTRL-Z jelet eltávolítja int fclose(FILE *stream) ; // fájl lezárás int feof(FILE *stream) ; // fájl vége van-e? int ferror(FILE *stream) ; // az utolsó fájl művelet hibakódját kérdezi le int fflush(FILE *stream) ; // a pufferek ürítése int fgetpos(FILE *stream, fpos_t *pos) ; // visszaadja az aktuális fájl pozíciót. int fsetpos(FILE *stream, fpos_t *pos) ; // a megadott helyre pozícionálja a fájlt. long ftell(FILE *stream) ; // fájl pozíció lekérdezése long fseek(FILE *stream, long int pos, int origin) ; // fájl pozíció beállítása a pos-ra long rewind(FILE *stream) ; // fájl pozíció beállítása előre // = fseek(file, 0, SEEK_SET) ;

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

Alexin Zoltán Programozás II., egyetemi előadás fóliák

114/114

size_t fread(void *puffer, size_t size, size_t count, FILE *stream) ; // bináris olvasás egy pufferbe, size*count bájt size_t fwrite(void *puffer, size_t size, size_t count, FILE *stream) ; // bináris írás egy pufferből, size*count bájt int fgetc(FILE *stream) ; // egy character beolvasása int getchar() ; // egy character beolvasása az stdin-ről int fputc(int c, FILE *stream) ; // egy character kiírása int putchar(int c) ; // egy character kiírása az stdout-ra char *fgets(char *str, int n, FILE *stream) ; // string beolvasás fájlból, a str-helyre, mérete max n int fputs(char *str, FILE *stream) ; // string kiírása fájlba, a str-ből int scanf(const char *format, [args]) ; // formázott beolvasás az stdin-ről int fscanf(FILE *stream, const char *format, [args]) ; // formázott beolvasás egy fájlból int sscanf(const char *str,const char *format,[args]) ; // formázott beolvasás egy stringből int printf(const char *format, [args]) ; // formázott kiírás az stdout-ra int fprintf(FILE *stream, const char *format, [args]) ; // formázott kiírás egy fájlba int sprintf(const char *str,const char *fmt,[args]) ; // formázott kiírás egy stringbe

Szegedi Tudományegyetem, Informatikai Tanszékcsoport

Szeged, 2011. december 12.

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF