A C++ programozás alapjai - segédlet
1 Rev 1.
Tartalomjegyzék 1 Bevezetés................................................................................................................................................................5 1.1 POSIX specifikáció......................................................................................................................................6 1.2 Fordítók és fejlesztői környezetek..........................................................................................................7 GNU-LINUX GCC.......................................................................................................................................8 Windows Cygwin.......................................................................................................................................8 Windows: MinGW......................................................................................................................................8 Az Eclipse IDE..............................................................................................................................................8 Telepítés Windows alat............................................................................................................................9 Az Eclipse használatának alapjai............................................................................................................9 1.3 Egy egyszerű példa...................................................................................................................................10 Hello World forrás....................................................................................................................................10 1.3.1 Megjegyzések, kommentek......................................................................................................11 1.3.2 Egyszerű input / output.............................................................................................................12 1.3.2.1 Kimeneti formátum módosítása:....................................................................................13 1.4 Fordítási lépések........................................................................................................................................14 1.5 Elemi adatipusok......................................................................................................................................15 Adatípusok, változók, és jellemzőik....................................................................................................15 1.5.1 Beépítet alaptípusok..................................................................................................................16 1.5.2 Egész számok...............................................................................................................................17 1.5.3 Lebegőpontos számok................................................................................................................18 1.5.4 Karakterek.....................................................................................................................................19 1.5.5 Logikai típus.................................................................................................................................20 1.5.6 Stringek..........................................................................................................................................21 1.5.7 Típuskonverzió...........................................................................................................................22 1.5.8 Implicit konverzió elemi tipusokon........................................................................................23 1.5.9 Változó nevek...............................................................................................................................24 1.5.10 Szimbolikus konstansok..........................................................................................................25 2 Kifejezések...........................................................................................................................................................26 2.1 Aritmetikai operátorok............................................................................................................................27 2.2 Relációs operátorok..................................................................................................................................28 2.3 Logikai operátorok....................................................................................................................................29 2.4 Bitenkénti operátorok..............................................................................................................................30 2.5 Auto inkrement - auto dekrement........................................................................................................31 2.6 Értékadó operátorok.................................................................................................................................32 2.7 Feltételes operátorok................................................................................................................................33 2.8 Vessző operátor.........................................................................................................................................34 2.9 sizeof operátor...........................................................................................................................................35 2.10 Pointerek...................................................................................................................................................36 2.11 Bővebben az elemi stringekről............................................................................................................37 2.12 Precedencia...............................................................................................................................................38 3 Utasítások, vezérlési szerkezetek...................................................................................................................39 3.1 Utasítások....................................................................................................................................................40 3.2 Egyszerű feltételvizsgálat........................................................................................................................41 3.3 Egymásba ágyazot, többszintű feltételvizsgálat...............................................................................42 3.4 Elágazásos feltételvizsgálat.....................................................................................................................43 3.5 Ciklusszervezés while utasítással.........................................................................................................44 3.6 Ciklusszervezés do-while utasítással...................................................................................................45 2 Rev 1.
4
5
6
7
8
9
3.7 Ciklusszervezés for utasítással..............................................................................................................46 3.8 Ciklusszervezés tartományra for utasítással......................................................................................47 3.9 A continue utasítás...................................................................................................................................48 3.10 A break utasítás.......................................................................................................................................49 3.11 A goto utasítás.........................................................................................................................................50 3.12 A return utasítás......................................................................................................................................51 Függvények.........................................................................................................................................................52 4.1 A függvények részei:................................................................................................................................53 4.2 Függvény deklaráció................................................................................................................................54 4.3 Függvény paraméterek és argumentumok.........................................................................................55 4.4 Hatókör........................................................................................................................................................57 4.5 Argumentumok alapértelmezet értéke..............................................................................................58 4.6 Parancssori argumentumok....................................................................................................................59 4.7 Függvények túlterhelése.........................................................................................................................60 4.8 Függvény pointerek..................................................................................................................................61 Összetet adatípusok........................................................................................................................................62 5.1 Tömbök........................................................................................................................................................63 Tömbök létrehozása, és hozzáférése....................................................................................................64 Tömbök mint függvényparaméterek...................................................................................................65 5.1.1 Egy dimenziós tömb átadása....................................................................................................66 5.1.2 Több dimenziós tömbök átadása.............................................................................................67 5.2 Struktúrák...................................................................................................................................................68 Az osztályokról általában................................................................................................................................69 6.1 Tagfüggvények, vagy metódusok.........................................................................................................70 6.2 Adatagok, vagy tagváltozók..................................................................................................................72 6.3 Hozzáférési engedélyek...........................................................................................................................73 6.4 Konstruktorok............................................................................................................................................74 6.5 Destruktorok..............................................................................................................................................75 6.6 Osztálytagok - osztályváltozók..............................................................................................................76 6.7 A this változó.............................................................................................................................................77 6.8 Inicializációs lista......................................................................................................................................78 6.9 Operátor túlterhelés.................................................................................................................................79 Öröklődés (származtatás, bővítés).................................................................................................................80 7.1 Barátosztályok (friendship)....................................................................................................................82 7.2 Többszörös öröklődés...............................................................................................................................84 7.3 Polimorfizmus............................................................................................................................................85 7.4 Absztrakt osztály:......................................................................................................................................86 7.5 Osztály template-ek..................................................................................................................................87 7.6 Implicit típuskonverzió............................................................................................................................88 7.7 inline Függvények.....................................................................................................................................89 További ismeretek az adatipusokról............................................................................................................90 8.1 Union-ok......................................................................................................................................................91 8.2 Enumeráció.................................................................................................................................................92 8.3 A fájlkezelés alapjai..................................................................................................................................93 8.4 Típusdefiníció.............................................................................................................................................94 8.5 Dinamikus memória kezelés..................................................................................................................95 8.6 Tárolási osztályok és típusminősítők...................................................................................................96 Egyéb osztálytulajdonságok............................................................................................................................97 9.1 Bináris operátor túlterhelésről részletesen.........................................................................................98 9.2 Konstans objektumváltozók...................................................................................................................99
3 Rev 1.
9.3 9.4 9.5 9.6 9.7 9.8
Konstans osztályváltozók......................................................................................................................100 Statikus tagfüggvények.........................................................................................................................101 Tagobjektumok........................................................................................................................................102 Objektum tömbök...................................................................................................................................103 Osztály láthatóság...................................................................................................................................104 Egyéb, fontosabb osztályok..................................................................................................................105 String osztály...........................................................................................................................................105 10 Preprocesszor..................................................................................................................................................106 10.1 Lépései:....................................................................................................................................................107 10.2 Direktívák:..............................................................................................................................................108
4 Rev 1.
1 Bevezetés
5 Rev 1.
1.1 POSIX specifikáció Portable Operating System Interface – olyan specifikáció, amely leírja egy általános Unix szerű operációs rendszer paramétereit. Alapvetően két témakörből áll: 1. POSIX Alapdefiníciók: – System interfészek és parancsok – Real-time szolgáltatások – Thread szolgáltatások – Biztonsági interfészek – Hálózatkezelés – Védelmi és vezérlő parancsok – Batch kezelő programok 2. POSIX megfelelőségi tesztek
6 Rev 1.
1.2 Fordítók és fejlesztői környezetek GNU-LINUX GCC Debian / Ubuntu disztribúciók esetén: – cli:sudo apt-get install gcc g++ – synaptic package manager – software centre
Windows Cygwin Www.cygwin.com Eredeti fejlesztője a Cygnus Solutions. Feladata: Teljes POSIX réteg megvalósítása könyvtárak és rendszerhívások emulálására. Ennél a megoldásnál a POSIX réteg folyamatosan fut a Windowsban, ami többlet CPU kapacitást igényel. A Cygwin segítségével lefordítot programokhoz a kompatibilitási réteget biztosító lib-et is mellékelni célszerű, hogy futathatók legyenek. Felépítése: – Library, ami a Unix (POSIX) rendszerhívásokat Win32-nek megfelelő rendszerhívásokat (API) valósítja meg – GNU development toolchain – nay számú, Unix-ra jellemző parancsot megvalósító alkalmazás (mount, sshd, Apache, grep, emacs, vi, sed, stb).
Windows: MinGW Www.mingw.org Minimalist GNU for Windows – a Cygwin első változatából ágazot le. A kompatibilitást direkt Windows hívásokra fordítja le. Nem biztosít teljes POSIX kompatibilitást, a teljes Unix programkínálat nem fordítható le a segítségével. Erőforrás igénye kisebb, nem igényli a kompatibilitási réteg folyamatos jelenlétét.
Az Eclipse IDE Www.eclipse.org Eredeti fejlesző: IBM Canada Kezelője 2004 óta az Eclipse Foundation. Java alapú fejlesztő környezet. Licence: EPL (Eclipse Public Licence) – FSF szerint free licence, de nem GPL kompatibilis. Felépítés:
7 Rev 1.
– Workspace – Plugin rendszer – Az Eclipse IDE
Telepítés Windows alatt 1. Java runtime install – Download http://www.oracle.com/technetwork/java/javase/downloads/java-se-jre-7-download4321555.html 2. MinGW (Minimal GCC for Windows) letöltés – www.mingw.org Downloads Installer: mingw32-base mingw32-gcc-g++ – www.mingw.org – PATH_ba beírni a MINGW/bin-t: 5. file explorer-ben ráálni a c:MINGW/bin-re, jobb gomb és copy 2. control panel: system and security -> system ->advanced fül, és environment variable. 3. PATH után beírni ; után a bemásolt útvonalat! 3. Eclipse CDT letöltés – Átnevezés a hosszú filenév elkerülése miatt eclipse.zip-re – Eclipse indítás. – New project -> C++ -> Hello world – Project properties -> c/c++ build -> Tool Chain Editor-ban MINGW-t beállítani Cross GCC helyett – Ha internal build helyett make-re állítjuk, a Project Properties -> C/C++ Build-ben a make-et migw32-make -re kell módosítani.
Az Eclipse használatának alapjai
8 Rev 1.
1.3 Egy egyszerű példa Hello World forrás #include
// preprocesszor direktíva int main(void) főprogram, függvény neve { std::cout << „Hello World\n“; //kiíratás } // utasításblokk vége
// // utasításblokk kezdete
#include – preprocesszor direktíva – A fordítás előtt bevágja a megadott fájlt mint szövegállományt a forrásba. – Az iostream.h ey u.n. header fájl, amely az iostream-mel kapcsolatos deklarációkat és definíciókat tartalmazza. Régebbi környezetekben iostream.h, újabban iostream. Int main() - a program legmagasabb szintű végrehajtandó függvénye A függvényeknek lehet 0, vagy több paramétere. – A void azt jelöli, hoy nincs paraméter. – Jelen esetben a függvény visszatérő értékének típusa integer. – Minden C++ programnak ey, és csak ey main() függvénye van. A programvégrehajtás a main()-en kezdődik. { - a függvény testének a kezdete (utasításblokk kezdete) A következő sor a main()-t alkotó utasítások sora. – Minden utasítást ;-vel kell lezárni. „Hello World\n“ string kiíratása a cout output stream-re. \n - újsor karakter jelölése. std:: - Standard IO névtere. cout - Standard output stream. << - túltelített operátor. Balértékként az output stream-et, jobbértékként ey kifejezéstkell megadni. } - A függvény testének a vége (utasításblokk vége) A kiterjesztés a UNIX szerű környezetben .cc, Windows környezetben cpp. (Helloworld.cc vagy Helloworld.cpp) Fordítás: g++ HelloWorld.cc – – – –
– A kimenő fájl jelen esetben a.out, de a -o fordítási opcióval tetszőleges kimenő fájlnév megadható.
9 Rev 1.
1.3.1 Megjegyzések, kommentek // általában tört soros megjegyzés /* általában hosszabb, akár több soros megjegyzés */
Alapvető szabályok: – A megjeyzések nem eymásba áyazhatóak. – A megjeyzés leyen érthetőbb, mint a programrészlet, amit leír! – Túl sok megjeyzés csökkenti a program olvashatóságát. – Beszélő szimbólumok használata csökkenti a megjeyzések számát. – A megjeyzéseket általában angolul írjuk! – "Mérőszám: WTF/minute"
10 Rev 1.
1.3.2 Egyszerű input / output
Operátorok: – << – kiírás stream-be – >> – beolvasás stream-ből A stream-ek az vagy az include fájlok segítségével érhetők el. A std:: névtérben vannak elkülönítve, tehát vagy a std:: névtér hivatkozással kell őket használni, vagy using namespace std; paranccsal. – cout – kiviteli stream – cin – beviteli stream
11 Rev 1.
1.4 Fordítási lépések
12 Rev 1.
2 Elemi adattipusok
13 Rev 1.
2.1 Adattípusok, változók, és jellemzőik A változók adatok átmeneti tárolására szolgálnak. A fordító a RAM-ban foglalt memóriatartományban helyezi el őket. Ez természetesen nem áll a const előtagú szimbólumokra. (Lásd később.) A változókat két atríbútummal jellemezzük. – Típus - a változó definiálásakor adandó meg, a továbbiakban nem lehet megváltoztatni. – Érték - tetszőleges időpontban újraírható. Definíció és értékadás – A változó első értékadása az inicializálás. – A példában a definíció és az értékadás elkülönül, de inicializáláskor ez eyüttesen is elvégezhető. – Inicializálás nélkül elvileg lehetséges használni a változókat, de ilyenkor az értékük véletlenszerű. Értékadás Ennek során a válozóhoz konkrét értéket rendelünk. A = 3; int a (3); int a {3};
// -std=c++11 vagy -std=gnu++11
Beépített alaptípusok Alaptípusok – int – egész – float – lebegőpontos – double – dupla pontosságú lebegőpontos – bool – logikai – char – karakter – wchar_t – „széles” karakter – void – típus nélküli Módosítók: – – – –
signed – előjeles unsigned – előjeltelen short – rövid tárolási mód long – hosszú tárolási mód
Egész számok Típusok: (Alapértelmezés: signed)
14 Rev 1.
– short int vagy short – tárolási eységre kell optimalizálni, de legalább 2byte – int – legalább 2 byte – long int vagy long – legalább 4byte – long long int vagy long long – legalább 8byte – unsigned... – signed... Literalok: Számérték valamilyen számrendszerben kifejezet alakja. 19 – int (alapértelmezés) 19L – long 19LL – long long 19U – unsigned 19LU – long unsigned ...
Lebegőpontos számok Típusok: (Alapértelmezés: signed) – float – 4byte – double – legalább annyi, vagy több byte, mint a float – 8byte – long double – legalább annyi, vagy több byte, mint a double – 8 vagy 10 byte Literalok: 0.06F- float szimpla pontosság 0.06
– double dupla pontosság
0.06L – long long double 2.132E-3 – exponenciális alak Számrendszerek ábrázolása 45 – decimális 045 – oktális 0X45 – hexa
Karakterek A karakter kódja – numerikus érték, a kódrendszertől függ. Legelterjedtebb: ASCII. Pl 'A' = 65, 'a' = 97 Alapértelmezet a signed, de lehet unsigned is. Típusok: (Alapértelmezés: signed)
15 Rev 1.
– char – 1byte. de legalább annyi helyiérték, hoy 256 érték tárolására alkalmas leyen! Ez alapján tárolhat ASCII vay UTF-8 kódot is. – wchar_t – 2 vagy 4 byte Előfordulhatnak még: – char8_t – char16_t – char32_t – uchar …
Példák: Signed char leter = 'A'; unsigned char row = 2, column = 4; Literalok: 'A' '\r' – carriage return '\n' – new line '\t' – vízszintes tabulator '\v' – függőleges tabulator '\b' – backspace '\f' – formfeed '\\' - backslash '\'' - aposztróf '\“' - idézőjel '\ooo' – oktális számmal ábrázolt ascii kód '\0' – null kódú karakter '\a' – alert '\?' - kérdőjel
Logikai típus Valójában int-tel egyenértékű típus. Értéke 0, vagy nem 0, de a jobb olvashatóság érdekében használható helyete a vele egyenértékű bool. Típusok: – bool - 1 byte Literalok: true false
Stringek A string változó értéke az a memória cím, ahol a string kezdődik. A stringet mindig \0 karakter 16 Rev 1.
zárja le: H E L L O \0 Típusok: (Alapértelmezés: signed) – char* – A hossz tetszőleges. Literalok: "Hello World\n" "Name\tAddress\tPhone" Több soros literal: "This is an example \ of a multi line literal" Figyelem! „A“ nem azonos 'A'-val! „“ valójában egy \0, de a típusa nem azonos a karakter típussal.
String osztály. Létezik egy standard string osztály is, ami a std névtérben a <string> header segítségével érhető el.
Típuskonverzió C stílusú típusmódosítás lehetséges bármikor, a kívánt új típust a változó, vagy érték neve elé írva zárójelben, vagy a típus után írva zárójelben a kifejezést. Példa: int i;
float f = 3.14; i = (int) f;
// eredmény: 3
... i = int (f); // C++-ban ez is elfogadot Ez a fajta C típusú típusmódosítás több lehetséges eljárást is tartalmaz. A C++ ezeket explicit módon el tudtja különíteni: i = static_cast (f);
Implicit konverzió elemi tipusokon Példa: short a=2000; int b; b=a;
Az automatikus konverzió helyes adatot hoz létre, ha nagyobb pontosságú típusba konvertálunk. Figyelem! Előjelesség esetén, ha előjeleset konvertálunk előjeltelenné, a ketes komplemens érték jelenik 17 Rev 1.
meg.
Változó nevek A változó névnek a következő követelményeknek kell eleget tenne: Tartalmazhat betűket 'A'-'Z' és 'a'-'z' tartományban. – – – –
Tartalmazhat számjeyeket '0'-'9' tartományban, de nem az első helyen. Tartalmazhat '_' karaktert. Nem tartalmazhat kulcsszavakat és műveleti jeleket. Hossza elvileg nem limitált, de yakorlatban általában 255 karakter.
Szimbolikus konstansok Const double PI = 3.14155926654; Definíciókor inicializálni! Ismételten már nem kaphat új értéket. Érvényes a const SIZE = 500; is, ekkor az alapértelmezett típus int. A konstanst a fordító több módszerrel kezelheti fordításidőben: immediate értékként, vay regiszterben tartott változatlan értékként (pl. 0 értékű regiszter az avr-g++ esetén) Függvényekkel kapcsolatos megjegyzések: – – – –
függvény bármelyik paramétere lehet konstans: int power (const int base, const int exponent); Stilisztikai megjegyzés: A konstansokat általába a header fájl-okban definiáljuk, hogy az értékük ismert legyen.
18 Rev 1.
2.2 Karakteres input / output, bővebben További csatornák: – cerr - standard kiviteli stream hibaüzenetek számára – clog - standard log output
Kimeneti formátum módosítása Manipulátorok segítségével lehetséges, melyeket az #include segítségével vehetünk igénybe. Ezek: setw(n): cout << setw(5) << 3.65354656363456234 << endl; // a nyomtatási oszlop szélessége Ez az egyetlen olyan manipulátor, amely csak a következő paraméterre érvényes. Az összes többi perzisztens, tehát mindaddig érvényben marad, amíg felül nem írjuk. Setprecision(n) cout << setprecision(2) << 3.65354656363456234 << endl; // a nyomtatot számjegyek száma Ebben az esetben az eredmény: 3.6 Ha csak a tizedes jegyek számát akarjuk állítani, előbb aktiválni kell a fixed flag-et. Ez NEM függvény! Cout << fixed << setprecision(2) << 3.65354656363456234 << endl; // a nyomtatot számjegyek száma setbase(n) cout << setbase(16) << 245; // hexadecimális kimenet left és right Flag-ek. Hasonlóan a fixed-hez, ezek sem függvények! Cout << left << setw(6) << 3; // a 6 széles oszlop bal oldalára igazít. Showpoint és noshowpoint Flag-ek. A lebegőpontos szám végét feltölti nullákkal a megkívánt pontosságig. Cout << fixed << setprecision(4) <<showpoint << 3.2; // eredménye: 3.20050
19 Rev 1.
3 Kifejezések Bármilyen számítási sorozat, aminek kiértékelhető eredménye van.
20 Rev 1.
3.1 Aritmetikai operátorok Operátorok: – – – – –
+ összeadás - kivonás * szorzás / osztás % maradék Figyelem!
– %-ot kivéve minden operátor int és real tetszőleges kombinációját elfogadja. % mindkét paramétere int. – Ha mindkét operandus int, az eredmény int. – Az egész osztás hozhat nem várt eredményt: 9 / 2 eredménye 4 (NEM 4.5)! -9 / 2 eredménye -5 (NEM -4)! – Ha legalább az eyik operandus real, az eredmény is real (pontosabban double). – Ha real eredményt akarunk: type cast (típusmódosítás), ellenkező esetben egészosztás történik. int dist = 500; int time = 50; double speed = dist / (double) time; – Ügyelni kell a túlcsordulásra! A következő művelet rossz eredményt ad unsigned char a = 64 * 4 // az eredmény 0 lesz. – 0-val osztás: fordítási időben az operandusokat nem ellenőrzi a fordító, 0-val osztás futásidejű hibát, és általában leállást eredményez.
21 Rev 1.
3.2 Relációs operátorok A relációs operátor operandusai számértékkel kell, hogy rendelkezzenek. Kiértékelés után egy bool értékkel ekvivalens. Operátorok: – – – – – –
== eyenlőség !!= nem eyenlőség <= kisebb eyenlő (sorrend!) >= nayobb eyenlő (sorrend!) < kisebb > nayobb Példa: 'A' < 'F' // helyes "HELLO" < "WORLD" // nem helyes
22 Rev 1.
3.3 Logikai operátorok Nincs valódi beépítet bool típus, eléggé elterjedt, hogy int-et használnak helyete. Operátorok: – !! negálás (unáris operátor) – && Logikai ÉS – || Logikai VAGY Figyelem! – Értéke valójában 0 vay nem 0 lehet. – Kiértékelés balról: ha a && baloldali feltétel nem teljesül, vay a || bal oldali feltétel teljesül, nem folytatódik a kiértékelés. Tehát a jobb oldali kifejezésnek ne szánjunk mellékhatást, mert nem biztos, hoy végre lesz hajtva! Példák: !130 // értéke 0 1000 && 3200 // értéke 1 1000 || 3200 // értéke 1 (0xaa & 0x55) && 255 // értéke 0 (false) if ( (i<10) && (++i
23 Rev 1.
3.4 Bitenkénti operátorok Operátorok: ~ bitenkénti negálás & bitenkénti ÉS | bitenkénti vay ^ kizáró vay << léptetés balra >> léptetés jobbra Mivel a léptetés az implementáció függő előjelet nem tudja egységesen kezelni, célszerű eleve előjeltelen vátozókon használni. Példák: – – – – – –
unsigned char x = '\011'; unsigned char y = '\045'; x: 00010001 y: 00100101 ~x: 11101110 x & y: 00000001 x & y: 00110101 x ^ y: 00110100 x << 1: 001000010 x >> 1: 000010000
24 Rev 1.
3.5 Auto inkrement - auto dekrement Prefix-ként: először műveletvégzés, majd az így nyert változót értékadáshoz használjuk Postfixként, elöször értékadás, majd művelet a változón. – ++ inkrement (prefix / postfix) – -- dekrement (prefix / postfix) Példák: int k = 10; int j = ++k; // j: 11, k: 11 int j = k++ // j: 10, k: 11
25 Rev 1.
3.6 Értékadó operátorok Érték letárolása valamilyen módon megadot memória címre. Az értékadó operátor egy olyan kifejezés, melynek értékét a bal oltali operandusban tárolni kívánjuk. – lvalue – írhatónak kell lennie, memória cím - eyelőre csak változó nevet láttunk példának. – rvalue – lehet változó, de lehet konstans (literal) is Tehát (lvalue = rvalue) hatására: 1. rvalue átmásolódik lvalue-be 2. visszatés az átmásolt értékkel, ami ismét használható rvalue-ként. Így lehetséges többszörös értékadás is. Az értékadást megelőzheti egy művelet (mellékhatással). – = eyszerű értékadás – += hozzáadás – ...ennek mintájára valamennyi bináris operátor használható az = jel bal oldalán! Példa: // a többszörös értékadás alapja, hogy az értékadés mellékhatásán kívül // visszatérő értéke is van. int j, k, l; j = k = l =10; // ( j = (k = (l = 10))); j += k = l = 10 // j = j + (k = l = 10);
26 Rev 1.
3.7 Feltételes operátorok Operátorok: operandus1 ? Operandus2 : : operandus3 – ha op5 nem 0: op2 kifejezést kell kiértékelni, és ez lesz a visszatérő érték, – ha op5 nulla: op3 kifejezést kell kiértékelni. és ez lesz a visszatérő érték. Példák: int j = 1, k = 2; int min = j < k ? j : k; // Mivel a feltételes operátorok végül egy kifejezést képeznek, // több is egymásba ágyazható. Int j = 1, k = 2, l = 3; int min = j < k ? (j < l ? j : l) : (k < l ? k : l);
27 Rev 1.
3.8 Vessző operátor Több kifejezés kiértékelése egyszerre. Visszatérő érték a legutolsó kif. Értéke. Azoknak a kifejezéseknek, melyek nem visszatérő értékek, természetesen csak akkor van értelmük, ha van mellékhatásuk. Példa: int j, k, min; int jCtr = 0, kCtr = 0; min = j < k ? jCtr++, j : kCtr++, k;
28 Rev 1.
3.9 sizeof operátor Operandusa egy típusnév, vagy egy kifejezés. A visszatérő érték implementáció függő.
29 Rev 1.
3.10 Pointerek Egy bizonyos memória hely címe. Értékére ritkán vagyunk kiváncsiak, többnyire hivatkozásként használjuk. Operátorok: – * operátor jelentése: "point to" – & operátor jelentése "address of" Példa: int* ptr; char* ptrChar; int num; ptr = #
Figyelem! – A * mint dereferencia operátor: argumentuma ey pointer, ami feloldja a hivatkozást, tehát *ptr ekvivalens lesz a num-mal. – A pointer definiálásakor meg kell adni, hoy milyen típusra mutat. – A pointer típusától függetlenül lehet neki 0 értéket adni. (neve: null pointer) – A pointer típusát lehet módosítani (type cast) pl. ptrChar = (char*) ptr; (csak indokolt esetben) – típusfüggetlen pointer: void* anyPtr; Pointerekkel kapcsolatos megjegyzések: const int* size = 38 // integer konstansra mutató pointer változó int* const age = 42 // konstans pointer, ami integer változóra mutat *size = 40;
// Hibás age = 0L;
*age = 40;
// Helyes size = 0L; // Helyes size = age;// Helyes
30 Rev 1.
// Hibás age = size; // Hibás
3.11 Mellékhatás és visszatérő érték Rendszerint a lényeg a kiértékelés során kapot eredmény, de a kiértékelés során más is történik: 1. kiszámítódik a számítási sorozat eredménye, 2. mellékhatások: néhány operátor a rendszer állapotában is változást idéz elő (pl. értékadás, inkrementálás, dekrementálás. Példa: cout << “Hello “ << “World“;
Balról jobbra lesz végrehajtva. Cout << “Hello “
mellékhatása nyomtatás, visszatérő értéke a cout stream címe. Így a (cout << “Hello “ ) << “World“
második lépésben már egyenértékű lesz ezzel: cout << “World“
31 Rev 1.
3.12 Bővebben az elemi stringekről Hozzunk létre két stringet: char* str1 = "firstname“; char* str2 = "lastname“;
A műveletet a következő módon lehet értelmezni: az „ (idézőjel) operátor mellékhatása, hogy a memóriában elhelyezi a megadot karakterláncot, és '\0' karakterrel lezárja. Ugyanakkor a visszatérő értéke az a memóriacím, ahová a stringet elhelyezte. Tehát az eredmény: két '\0'-val lezárt karakterlánc a memóriában, és két pointer típusú változó, amely a karakterláncok kezdőcímét tartalmazza. A C++11 óta azt is jelezni kell, hogy a stringet csak olvasható memóriaterületre kell elhelyezni, tehát: const char* str1 = "firstname“; const char* str2 = "lastname“;
Ebből ugyanakkor az is következik, hogy a: str1 = str2;
nem a string tartalmát tölti át, csak egyik pointert a másikba. Ugyanakkor : str1 = "middlename“;
Memóriában létrehoz egy stringet „middlename“ és a címét str1-be tölti. A memóriában levő string átírása elvileg a következő függvénnyel végezhető el: <string.h> strcpy(char* dest, char* source)
Azonban a dest csak olvasható memóriaterületre mutat, így ez a függvény nem működhet. A megoldás:
32 Rev 1.
3.13 Precedencia
33 Rev 1.
4 Utasítások, vezérlési szerkezetek
34 Rev 1.
4.1 Utasítások Szekvenciális műveletek mellékhatással. 1. Egyszerű utasítás: egyetlen utasítás : --vel lezárva. Példa: int i; // egyszerú utasítás: vált definíció ++i; // művelet (mellékhatással) i + 8; // ; -vel lezárt kifejezés – művelet (mellékhatás nélkül) ; ; // null utasítás 2. Összetet utasítás, vagy blokk: {} közé zért egyszerű utasítások. – Lehetővé teszik, hoy több utasítás írjunk oda, ahová eyébként csak eyet lehetne – Korlátozzák az érvényességi kört (scope) Típusok: – deklarációk – hozzárendelés (értékadás) műveletek – feltételes elágazások – ciklusszervező utasítások – végrehajtás vezérlők
35 Rev 1.
4.2 Egyszerű feltételvizsgálat If (kifejezés) utasításblokk1; else utasításblokk2; Példa: if (account > 0) { interest = account * debitPercentage; account = account + interest; // kiemelhető } else { interest = account * creditPercentage; account = account + interest; }
Példa feltételes kifejezéssel egyszerűsítve: account += account ? ( account * debitPercentage : account * creditPercentage);
Példa további egyszerűsítéssel: account += account * (account ? debitPercentage : creditPercentage);
36 Rev 1.
4.3 Egymásba ágyazott, többszintű feltételvizsgálat if (kifejezés1) { utasítás1; } else if (kifejezés2) { utasítás2; }
Vagy: if (kifejezés1) utasítás1; else if (kifejezés2) utasítás2;
37 Rev 1.
4.4 Elágazásos feltételvizsgálat Valójában használható lenne helyete az if – else if szerkezet, de a switch olvashatóbb. switch (kifejezés) { case constant1: utasítások1; break; case constant2: utasítások2; break;
... default: utasításokn; break; }
Példa: A, S, K, L mint kurzormozgató billentyűk: switch (key) { case 'a': ++y; break; case 's': --y; break; case 'k': ++x; break; case 'l'; --x; break; default: cout << "Error\n"; break; }
Viyázat! – A break elhayása nehezen ellenőrízhető hibát okoz. – Az utolsó break után beírt utasítást nem jelzi hibának - de nem is hajtja végre.
38 Rev 1.
4.5 Ciklusszervezés while utasítással while (kifejezés) { utasítások; }
– kifejezés: ciklusfeltétel – utasítások: ciklus törzs Figyelem! Végtelen ciklust a rendszer nem ellenőrzi
39 Rev 1.
4.6 Ciklusszervezés do-while utasítással do { utasítások; } while (kifejezés);
– ritkábban használt – akkor célszerű, ha mindenképp le kell eyszer futtatni a ciklust Figyelem! Végtelen ciklust a rendszer nem ellenőrzi
40 Rev 1.
4.7 Ciklusszervezés for utasítással Leggyakoribb felhasználás: lépéses ciklus – ilyenkor inkább javasolt, mint while for (kifejezés1; kifejezés2; kifejezés3) { utasítások; } kifejezés1 kiértékelésre kerül 3. Minden futási ciklusban ha kifejezés2 kiértékelésre kerül, 4. ha kifejezés2 nem 0, kifejezés3 és utasítások végrehajtása. Példa: long sum; // hatóköre kívül van a for ciklus törzsön. for (short i = 1; i < n; ++i) { // i hatóköre kívü a cilustörzsön. sum += i; } – ciklusváltozó hatóköre nem a ciklusmag, hanem maga a ciklus. – bármelyik kifejezés lehet üres – for utasítások eymásba áyazhatóak – minden kifejezés helyére több is beírható, ha használjuk a ”,” (vessző) operátort. Természetesen csak az utolsó ad vissza értéket, de pl. több ciklusváltozót íy lehet eyszerre inicializálni. Példák: for ( ; i != 0; ) // megfelel: while (i != 0) for (;;) // megfelel: végtelen ciklus (üres feltétel = true) for (i = 0, j = 0; i + j < n; ++i, ++j) { utasítások; } Vigyázat, gyakori hiba: for (...); // a ";" miatt a ciklus további része csak egyszer fut le. {
... } – CycleDemo.cc – Sum.cc
41 Rev 1.
4.8 Ciklusszervezés tartományra for utasítással Megadható egy halmaz, amin elemenként megy végig. For ( declaration : range ) statement; Példa: string str {“Hello!“}; for (char c : str) { std::cout << c; } std::cout << '\n';;;
42 Rev 1.
4.9 A continue utasítás Kihagyja a ciklusmag további végrehajtását és új ciklusfejet kezd. Példa: // Páros számok összeadása n-ig for (i = 0; i < n; ++i) { if ((n % 2) != 0) // elég lenne if (n % 2) is, de így olvashatóbb continue; sum += i;
}
43 Rev 1.
4.10 A break utasítás Megszakítja a ciklus, vagy a switch futását. Példa: „gondoltam egy számot – 3 kisérlet“ for (i = 0; i < 3; ++i) { cin >> i; if (i == n) { break; } cout << "Wrong!\n"; } if (i == n) cout << "gotja\n"; else cout << "looser\";
44 Rev 1.
4.11 A goto utasítás Alacson szintű ugró utasítás. Példa: for (i = 0; i < try; ++i) { cout << „Enter password: „; cin >> pw; if (Verify(pw)) // check password for correctness goto out; // drop out of the loop cout << „Wrong!\n“; } out: …
45 Rev 1.
4.12 A return utasítás Visszatérő érték generálása. Eddig csak a main() ben használtuk. Return kifejezés;
46 Rev 1.
5 Függvények Műveletszekvenciák csoportosítása.
47 Rev 1.
5.1 A függvények részei: – prototípus, vay interfész (visszatérő típus, név, paraméterek vay aláírás) – törzs (utasítások) Használat: hívás. – argumentumok kiértékelése (minden argumentum ey a megfelelő paraméterrel azonos típusú kifejezés) – a fenti kifejezések eredményeinek hozzárendelése a megfelelő paraméterekhez – függvénytörzs végrehajtása – visszatérő érték átadása a hívónak Figyelem! – nem-void függvény: kifejezés – void függvény: utasítás. Példa int power (int base, unsigned int exp) // prototype: base és exp lokális változók { int result = 1; // lokális változó for (int i = 0; i < exp; ++i) // végrehajtás result *= base; return result; // visszatérő érték } int main (void) { cout << "2 on the power of 3 is" power (2, 3); függvényhívás }
48 Rev 1.
//
5.2 Függvény deklaráció – – – – –
A függvényt mindíg csak akkor szabad használni, ha már deklarálva volt. Definíció maga is deklaráció, ha elől van, de javasolt kiyűjtött deklarációk használata. Deklaráció - prototype-pal. Deklaráció interfészébe konkrét változó nevek nem kellenek, de javasoltak. A paraméter listában minden paraméternek kell típus, vesszővel nem lehet több azonos típusú paramétert felsorolni.
49 Rev 1.
5.3 Függvény paraméterek és argumentumok Két paraméter típus C++ -ban (vegyesen is használható egy paraméter listában): Érték szerinti átadás: a függvény lemásolja az argumentumot (változások nem hatnak vissza). – Referencia szerinti átadás: pointerrel, vay referenciával. A függvény hozzáférést kap a paraméterhez, és változtathat rajta. Az alábbi példákban az increment függvény feladata, hogy a neki átadot változó értékét megnövelje. Erre a célra érték szerinti paraméterátadást nem használhatunk, mert az inkrementálás a függvénytörzsben csak az argumentum másolatán történik meg. Példa – átadás érték szerint változóban – ROSSZ! void increment (int number); int main() { int num = 4; increment(num); cout << num << '\n'; // kiíratás: 4} void increment (int number) // rossz, mert itt number másolatán fog dolgozni { + +(number); } Példa – pointer szerinti átadás void increment (int* nptr); int main() { int num = 4; int* numptr = # increment(numptr); cout << num << '\n'; } void increment (int* nptr) { ++(*nptr); } Példa – referencia szerinti átadás void increment(int& number); int main() { int num = 4; increment(num); cout << num << '\n'; } void increment(int& number) { ++number; }
50 Rev 1.
5.4 Hatókör Globális: – Élettartamuk a program teljes futási ideje. – Minden ami a program hatókörében van definiálva (tehát nem függvényben, vay osztályban, vay szeparált blokkban. – Inicializálatlan globális változók értéke 0-ra van automatikusan beállítva. – Mivel a teljes programból láthatók, névadás leyen eyedi! Lokális: – Élettartamuk csak a blokk elejétől a végéig tart. – Minden, ami utasításblokkon belül van. – Függvényparaméterek a függvénytörzsön belül lokálisak. – Névadásnak csak a blokkon belül kell eyedinek lenni. – Ha a hatókörök eymásba áyazódnak, a belső névadása felülírja a külsőt. – Ha felülírt globálisra akarunk hivatkozni, azt a :nnnév jelöléssel lehet. Példa: int xyz;
// xyz globális
void fnc (int xyz)
// xyz lokális a fnc-n belül
{ if (xyz > 0) { double xyz;
// xyz lokális a blokkon belül
//... } ; ::xyz = 0; // hívatkozás a globális változóra }
51 Rev 1.
5.5 Argumentumok alapértelmezett értéke A default argumentumokat mindíg a paraméterlista végén kell felsorolni! Célszerű meghatározás a deklarációban. Lehet a definícióban is, de soha nem mindkét helyen! Példa: int main() { int a = 4; int b = 5; cout << add(a, b) << „, „; // teljes paraméterlista cout << add(a) << endl;
// második argumentum elhagyva
} int add(int number1, int number2) { return number1 + number2; }
52 Rev 1.
5.6 Parancssori argumentumok Parancssori konzol segítségével indítot programnak szüksége lehet paraméterekre. A main() függvénynek két -féle definíciója létezik: int main(void); int main(int argcj, char* argv[]); Az argc azt adja meg, hogy hány paramétert kapot a program a parancssorban. A második paraméter egy string tömb, aminek első, tehát 0 indexű eleme magának a programnak a neve, a többi pedig a parancssorban beérkezet paraméterek. Például a sum 2 3 4 parancs: argc = 3; argv[0] értéke: „sum“, argv[1] értéke: 2 stb. Példa: int main(int argc, int *argv[]) { int sum = 0; for (int i = 1, i < argc; ++i) sum += argv[i]; cout << sum << '\n'; return 0; }
53 Rev 1.
5.7 Függvények túlterhelése A programot ovashatóbbá teszi, ha az azonos feladatot ellátó függvények azonos nevet kapnak. A C++ megengedi a függvények túlterhelését, azaz ugyanazon függvénynéven több függvény definiálását. A feltétel, hogy a függvényeket az argumentumok és a visszatérő érték típusai, tehát az u.n "aláírás" egyértelműen megkülönböztessék. Példák: long getTime(); void getTime(long* time); void getTime(int* hours, int* minutes); void getTime(int& hours, int& minutes);
54 Rev 1.
5.8 Függvény pointerek Lehetséges a függvény címét letárolni és hívatkozni rá. 1. Legyen több olyan függvényünk, aminek két int paramétere van és int-tel tér vissza: int f1 (int a, int b) {...} int f2 (int c, int d) {...} 2. Definiáljunk egy pointer típusú változót, ami egy ilyen függvényre mutat: int ( * fptr) (int, int) 3. Adjunk az fptr-nek értéket: fptr = &f1; De elegendő: fptr = f1; 4. Hívás: int result = fptr(5, 7); Példa: int increment(int a) {return a+1;} int main() { int (*fncPtr)(int); // fncPtr = &increment; helyet elegendő: fncPtr = increment; cout << fncPtr(3) << endl; return 0; }
55 Rev 1.
// eredmény: 4
6 Összetett adattípusok
56 Rev 1.
6.1 Tömbök Tömbök létrehozása, és hozzáférése Azonos típusú elemek halmaza. Hivatkozás sorrend alapján: tomb[index] Index 0...size-5 Neve csak a tömbnek van, az elemeknek nincs. Elemek összes lehetséges száma: dimenzió - nem keverendő össze a dimenziók számával. A dimenzió futás közben nem változtatható, mert fordításidőben kerül a memória lefoglalásra. Dimenzió túllépés eredménye: jó esetben futásidejű hiba generálódik, rossz esetben kiszámíthatatlan. Definíció: – – – – – –
int a[3]; Figyelem! Dimenzióban ugyanaz, mint int* a; de ezt nem lehet definícióként használni, mert nem tudni, hogy mennyi memóriát kell foglalni. Inicializáció int a[3] = {2, 4, 6}; int a[3] = {2, 4}; // a[2] értéke 0 int a[] = {2, 4, 6}; // a[] mérete 3 Hivatkozás A tömb neve index nélkül egy pointert ad vissza, ami a tömb első elemére mutat, de csak ha ez az egyszerűsítés nem egy olyan elemre mutat, ami maga is pointer (pl. két dimenziós tömb). Példa: int a[] = {2, 4, 6, 8}; int* aptr = a; cout << a[0] << ',' << *aptr << endl; cout << a[1] << ',' << *++aptr << endl; // a két kiíratás ugyanazt adja cout << a[1] << ','<< aptr + 1 << endl; // nem ugyanaz, mert prioritásosabb, mint + cout << a[1] << ',' << *(aptr + 1) << '\n'; // így már ugyanazt adja Stringek A string egy karaktertömb. Lehetséges inicializálás: char name[] = {'H', 'E', 'L', 'L', 'O'}; // mérete 5 - nem string!!!! char name[] = {"HELLO"} // mérete 6, mert a '/0' is a végére kerül. Ez a tényleges string.
Többdimenziós tömb A több dimenziós tömb tárolása valójában sorfolytonosan történik. int a[2][3] = { 2, 4, 6, 8, 10, 12, };
Sorfolytonos tárolás int a[2][3] = {2, 4, 6, 8, 10, 12};
Pointer aritmetika 57 Rev 1.
A pointerhez lehet egész számot hozzáadni, vagy abból elvenni.
Tömbök mint függvényparaméterek
58 Rev 1.
6.1.1 Egy dimenziós tömb átadása
Pointer segítségével Mivel az egy dimenziós tömb neve a [] nélkül egyenértékű egy a 0. elemére mutató pointerrel, használhatjuk ezt paraméter átadásra. Void f1(int *a) { ... }
Átadhatjuk a tömböt a pontos méretével együtt: Void f2(int a[10]) { ... }
A tömb méret nélkül is átadható: Void f3(int a[]) { ... } Mindhárom fenti példa egyenértékű, mert a C++ a tömböket alapértelmezés szerint a pointerükkel adja át, és nem az értékükkel. FIGYELEM! A tömbök kezelésénél nincs index határ figyelés, tehát ezt magunknak kell megoldani! A fenti függvények használata ezután mindhárom esetre: int main() { int b[10] = {10, 11, 12, 13, 14}; cout << f1(b); }
59 Rev 1.
6.1.1 Több dimenziós tömbök átadása int f1(int *a, int b[3][4])
{
return a[0] + b[0][0];
} vagy az első paraméter elhagyható: int f1(int *a, int b[][4])
{
return a[0] + b[0][0];
} Használata: int main(int argc, char *argv[])
{
int x[10] = {1, 2, 3}; int y[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; cout << "f1: " << f1(x, y) << endl; return 0;
}
60 Rev 1.
6.2 Struktúrák Valójában hagyaték a C nyelvből. Struct típusnév { típus1 vátozónév1; típus2 változónév2; ... } objektumnév ; – A struktúra létrehoz ey új típusnév típust. – Ha csak ey példányt akarunk, nem kell típusnév, az objektum azonnal létrehozható (objektumnév). Ekkor természetesen nem kapunk típusnevet, ami a továbbiakban használható lenne újabb uyanilyen típusú objektum létrehozására. Példa: Person postman = { „Robert“, 55}; // hívatkozás struktúra mezőre struktúrán keresztül: cout << postman.name << endl; // hívatkozás struktúra mezőre pointeren keresztül: Person *ptrPostman = &postman; cout << (*ptrPostman).name << „, „ << ptrPostman→age << endl;
61 Rev 1.
7 Az osztályokról általában Objektum: az OOP alapeszköze. Az OOP egyik alapelve a „becsomagolás“ vagy „encapsulation“. Lényege: egy objektumot alkozó összes tulajdonságot egy egységbe tömörítjük. Az objektumokat nem egyenként hozzuk létre, hanem egy előre definiált osztály példányosításával. Az osztály egy típust hoz létre, ami a beépítet típusokkal azonos módon használható. A struktúra és a union lényegében speciális osztályok. C++-ban osztály a struct és a class kulcsszóval egyaránt létrehozható. Alkotóelemei: – tagfüggvények, vay metódusok – adattagok, vay tagváltozók. Felépítés: – Fejléc (header): Osztálynév és alaposztályok (ezekből származtathatjuk az új osztályt). – Törzs (body): Az osztály tagjainak (members) felsorolása Példa: class Point { private: // a default értelmezés: private int xCoord, int yCoord; bool active; public: void set(int, int); void move(int, int) };
– Az osztályokat rendszerint a header file-ok tartalmazzák. – A változóktól eltérően az extern minősítő használata osztályoknál nem szükséges. – A jó olvashatóságot segíti, ha a deklarációk az osztályban (.h modulban), de a definíció a .cc modulban van. – Nem kötelező, de eyre elterjedtebb yakorlat az ey osztály - ey modul elv.
62 Rev 1.
7.1 Tagfüggvények, vagy metódusok Az osztályra jellemző műveletek. – Alkalmazhatók default argumentumok, és túlterhelhetőek, de viyázni kell, hoy a híváskor a default argumentumok elhayásával ne álljon elő olyan forma, ami az eyik túlterhelt változatot is kielégíti. – A tagfüggvényeket az osztályban deklaráljuk. A definíció történhet ekkor is, vay később, az osztályon kívül. Példa osztályon kívüli definícióra: void Point::set(int x, int y) { xCoord = x; yCoord = y; } void Point::move(int x, int y) { xCoord += x; yCoord += y; } // hívatkozás Point pt; pt.set( 15, 20); pt.move(10, 10); // azonban pt.xCoord = 10; // nem szabályos, mert xCoord privát változó Egyszerűbb metódusokat célszerű inline-nak definiálni! // Osztály a header (.h) fájlban: class Point { private: // a default értelmezés: private int xCoord, yCoord; bool active; public: inline void set(int x, int y) // inline függvény definiálható az osztályban { xCoord = x; ycoord = y; } void move(int, int); inline int getX() {return xCoord;} // osztálydeklarációban definiálva inline int getY() {return yCoord;} // osztálydeklarációban definiálva }; // osztályon kívüli definíció a forrás (.cpp) fájlban void Point::move(int x, int y) { xCoord += x; yCoord += y; }
63 Rev 1.
7.2 Adattagok, vagy tagváltozók A változó definíciókkal azonos szintaxis.
64 Rev 1.
7.3 Hozzáférési engedélyek – – – – –
public: bárki által hozzáférhető private: csak az osztályon belül hozzáférhető protected: csak az osztályon, és a belőle származtatott osztályokon belül hozzáférhető Ha az osztályt a class kulcsszóval hozzuk létre, az alapértelmezés: private, ha struct kulcsszóval, akkor az alapértelmezés public.
65 Rev 1.
7.4 Konstruktorok Az objektumpéldány létrejötekor automatikusan lefut. – – – –
Neve uyanaz, mint az osztályé. Paraméterlistája lehet változó (túlterhelt). Visszatérő értéke nincs Hozzáférés: public
Példa: Point::Point(int x, int y) { xCoord = x; yCoord = y; } Point::Point() { xCoord = 0; yCoord = 0; } // hívatkozás: Point p(10, 25); Point p();
66 Rev 1.
7.5 Destruktorok Jellemzően akkor használjuk őket, ha az objektumnak a megszűnése előt meg kell szüntetnie az objektumon belül végzet memóriafoglalásokat (new – delete operátorok), vagy le kell zárni az objektum által megnyitot erőforrásokat. Destruktorból csak egy van.
67 Rev 1.
7.6 Osztálytagok - osztályváltozók Az osztály tagjai deklarálhatók úgy is, mint static. Ebben az esetben az osztály példányosítása előt, a definíció pillanatában létrejön egyetlen példány ezekből a tagokból. A továbbiakban minden létrejöt példány ugyanazon egy osztályváltozót fogja használni. – Hívatkozás: mivel nem példányhoz (objektumhoz) kötött, nem lehet rá objektumon keresztül hívatkozni, ezért az osztály nevén keresztül kell :: operátorral. – Tagfüggvényre is megadható, de ennek csak akkor van értelme, ha az osztályt eyáltalán nem akarjuk példányosítani, hanem csak enkapszulációt akarunk. Eyetlen előnye, hoy a példányosítás esetenként néhány bájttal több memóriát igényel. 8 bites mikrokontrollereknél elterjedt vélemény hoy memóriát takarít meg - nem feltétlenül igaz. Akkor célszerű alkalmazni, ha kimondotan az osztályhoz kapcsolódóan akarjuk használni. Pl. példányok számlálása: Példa: class Point { public: static int objCtr; // példány számláló int xCoord, yCoord; Point(int x, int y) // objCtr-t it nem szabad inicializálni!!! { objCtr++; // számláljuk a példányt xCoord = x; yCoord = y; } } // A static változót külön meg kell definiálni, mert a példányosításból kimarad. // Ez ugyanabban a szkópban lehetséges, ahol a tagfüggvények definíciója. Int Point::objCtr = 0; // automatikusan is erre van beállítva // példányosítás: Point p1; Point p2; Point*ptrP2 = &p2; cout << p1.xCoord << '\n'; // objektumváltozó közvetlen hívatkozás cout << ptrP2→xCoord << '\n'; // objektumváltozó indirekt hívatkozás cout << Point::objctr << '\n'; // osztályváltozó hívatkozás
68 Rev 1.
7.7 A this változó Az adot objektumon belül használható private változó. – Típusa: pointer, ami olyan konkrét típusú, mint maga az objektum. – Az adott példányra mutat. – Csak tagfüggvényekben érhető el.
69 Rev 1.
7.8 Inicializációs lista Class Point { private: int xCoord, yCoord; public: Point (int x, int y) {
// Konstruktor xCoord = x; yCoord = y; } }
Helyete célszerűbb: class Point { private: int xCoord; int yCoord; public: Point(int x, int y) : xCoord(x), yCoord(y) {} } – Az inicializációs listát meg lehet adni az osztály deklarációban, vay a konstruktor osztályon kívüli definíciójában is.
70 Rev 1.
7.9 Operátor túlterhelés Az elemi operátorok kibővíthetők, hogy osztályokon végezzenek műveletet: – Unáris operátor esetén az újradefiniált operátor nem kap argumentumot, hanem a tagváltozókat értékeli ki. – Ha ey operátornak csak unáris vay, csak bináris formája van, akkor nem terhelhető túl, a másik formában. Példa ex66_class_operator: class Point { public: int xCoord, yCoord; // public, hogy ki lehessen íratni Point(int x, int y): xCoord(x), yCoord(y) {} // összeadás operátor Point operator + (Point &point) { return Point(xCoord + point.xCoord, yCoord + point.yCoord);} // negálás (unáris) operátor túlterhelése // az unáris operátor nem kap argumentumot, csak az osztálytagokon dolgozik Point operator - () { return Point(-xCoord, -yCoord);} }; int main() { Point p1(2, 2); Point p2(4, 4); Point p3 = p1 + p2; cout << p3.xCoord << „, „ << p3.yCoord << endl; p3 = -p3; cout << p3.xCoord << „, „ << p3.yCoord << endl; return 0; }
71 Rev 1.
8 Öröklődés (származtatás, bővítés) Új osztályok hozhatók létre egy kiválasztot alaposztály tagjainak a bővítésével. Ehhez megadjuk az alaposztályt, és a hozzáadandó új tagokat. Ezenközben az alaposztályhoz történő hozzáférést lehet korlátozni. Class új_osztály : public/protected/private alap_osztály { … } Az alaposztály legmagasabb szintű hozzáférései a megadot szintre lesznek bekorlátozva. – public: Az alaposztály public tagjai public lesznek. – protected: Az alaposztály public tagjai protected lesznek. – private: Az alaposztály minden tagja private lesz. Példa ex64_class_inheritence: class Polygon { protected: int width, height; public: void set(int w, int h) {width=w; height=h;} }; class Rectangle: public Polygon { public: int area () {return width * height;} }; class Triangle: public Polygon { public: int area () {return width * height / 2;} }; int main () { Rectangle rect; Triangle trgl; rect.set(4, 5); trgl.set(4, 5); cout << rect.area() << '\n'; cout << trgl.area() << '\n'; return 0; } A származtatot osztály nem örökli az alaposztálytól: a konstruktorokat, destruktorokat, – a túlterhelt operátorokat, – a barát osztályokat. Bár a konstruktorok nem öröklődnek, azok mégis meghívódnak – legalábbis az alapértelmezés szerinti, vagyis paraméter nélküli változat. Ha más konstruktorra van szükség, azt külön inicializáló listával kell hívni. Példa ex65_class_inher_initlist: class Polygon { protected: int width, height; public: Polygon(int w, int h): width(w), height(h) {} // konstruktor void set(int w, int h) {width=w; height=h;} }; class Rectangle: public Polygon { public: 72 Rev 1.
Rectangle(int w, int h): Polygon(w, h) {} int area () {return width * height;} }; class Triangle: public Polygon { public: Triangle(int w, int h): Polygon(w, h) {} int area () {return width * height / 2;} }; int main () { Rectangle rect(4, 5); Triangle trgl(4, 5); cout << rect.area() << '\n'; cout << trgl.area() << '\n'; return 0; }
73 Rev 1.
8.1 Barátosztályok (friendship) A barátosztály olyan – egyébként teljesen független – osztály, melynek hozzáférése van a private vagy protected szintű tagokhoz. Class Square; // kell, mert a Rectangle mindjárt hivatkozik rá // Téglalap osztály class Rectangle { private: int width, height; public: int area () {return (width * height);} void convert (Square square); // a téglalapot egy megadot négyzetel azonos méretűre változtatja }; // Négyzet osztály class Square { friend class Rectangle; private: int side; public: Square (int a) : side(a) {} }; void Rectangle::convert(Square square) { width = square.side;
// side-hoz csak azért férhet hozzá, mert friend
height = square.side; } int main () { Rectangle rect; Square sqr (5); rect.convert(sqr); cout << rect.area() << endl; return 0; 74 Rev 1.
} A példában a rect.convert-nek megadható egy square objektum, minek hatására a rect-et egy ugyanilyen oldalhosszúságú négyzeté konvertálja. A négyzet (square) oldala ugyan private, de a friendship miat a rect.convert hozzáférhet.
75 Rev 1.
8.2 Többszörös öröklődés A C++ lehetővé teszi több osztály tulajdonságainak az átszármaztatását is: class új_osztály : public/protected/private alapo1, public/protected/private alapo2 { ... } Figyelem! – Amennyiben a két alaposztályban uyanaz a tag előfordul, mindenütt az alapo1::tag illetve alapo2::tag módon kell rá hivatkozni!
76 Rev 1.
8.3 Polimorfizmus Ha egy osztály metódusa a virtual kulcsszóval kerül definiálásra, akkor a származtatot osztályokban ez a metódus újradefiniálható. class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area () { return (0); } }; // származtatott osztály: class Rectangle: public Polygon { public: int area () // úradefiniálva { return (width * height); } };
77 Rev 1.
8.4 Absztrakt osztály: Megtehetjük, hogy area()-t a Polygon-ban egyáltalán nem definiáljuk. Ekkor ezt jelölni kell: virtual int area () =0; // tisztán virtuális függvény Ez esetben Polygon u.n. absztrakt osztály, a származtatot osztályokban pedig mindenképp kell definiálni a metódust.
78 Rev 1.
8.5 Osztály template-ek Az osztályok generálását automatikusan is elvégezhetjük olyan módon, hogy ehhez típusnevet adunk meg paraméternek. Template definíció: template class Coordinate { public: T xCoord, yCoord; // a nyomtatás érdekében public Coordinate (T xarg, T yarg) // konstruktor { xCoord = xarg; // fontos, hogy = legyen definiálva T-re! XCoord = yarg; } void stepUp () {++xCoord;}
// fontos, hogy ++ legyen definiálva T-re!
};
Hivatkozás: Coordinate intCoord (5, 5); intCoord.stepUp(); cout << intCoord.xCoord << endl; Megtehetjük azt is, hogy bizonyos típusokra másképp definiáljuk az osztálytemplate-et. Pl. double típusú paraméter esetén stepUp() helyet legyen jumpUp(): template <> class Coordinate <double> { public: double xCoord, yCoord; Coordinate(double xarg, double yarg ) { xCoord = xarg; yCoord = yarg; } void jumpUp() { xCoord += 10.0;} };
Hivatkozás: Coordinate<double> doubleCoord(5.0, 5.0); doubleCoord.jumpUp(); cout << doubleCoord.xCoord << endl;
79 Rev 1.
8.6 Implicit típuskonverzió Publikus öröklés esetén implicit típuskonverzió megy végbe, tehát: class Ember {}; class Benzinkutas: public Ember {}; // a benzinkutas ember class Parkoloor: protected Ember {}; // a parkolóőr nem ember – Nem publikus öröklés esetén lehetséges az explicit típuskonverzió, de ekkor a programozó felelőssége, hoy ezt helyesen teye.
80 Rev 1.
8.7 inline Függvények Abszolutérték számításra a (n > 0 ? n : -n) kifejezés használható. Ha ezt többször használjuk, célszerű ezt egy függvényben definiálni: int Abs (int n) { return n > 0 ? n : -n; } A fenti megoldás helyet célszerűbb az inline int Abs (int n) { return n > 0 ? n : -n; } Ha a függvény inline-ként van definiálva, a fordító megpróbálja befordítani minden egyes előforduláshoz a függvényt, tehát bemásolni annak végrehajtható kódját, és nem hívással intézni. Ezáltal a hívással járó adminisztráció megtakarítható. Figyelem! – C++-ban az osztályon belül deklarált és definiált függvény alapértelmezése inline. – A compiler fiyelmen kívül hayhatja az inline kulcsszót, ha túl nay függvényről van szó. – inline függvényt csak akkor érdemes definiálni, ha az nayon rövid végrehajtható kódot eredményez, ellenkező esetben a hívással járó adminisztrációt uyan megtakarítjuk, de a hosszabb végrehajtható kódot sokszorosítjuk. – az inline függvény minden hívást megelőzően kell, hoy definiálva leyen. Ennek legjobb helye a header fájl, ami viszont rontja a program szerkezetét, és sérti az „encapsulation” elvet.
81 Rev 1.
9 További ismeretek az adattipusokról
82 Rev 1.
9.1 Union-ok Speciális struktúra, melyben a tagok ugyarra a memória címre vannak elhelyezve. Union DataField{ int data; unsigned char d[4]; } mydata; mydata.data = 2014; // hozzáférés byteonként
// használat int – ként cout << mydata.d[0];
– Létrehoz ey új DataField tipust – Ha csak ey példányt akarunk, nem kell típusnév, az objektum azonnal létrehozható (mydata). Ekkor természetesen nem kapunk típusnevet, ami a továbbiakban használható lenne újabb uyanilyen típusú objektum létrehozására.
83 Rev 1.
9.2 Enumeráció Szimbolikus konstansok elfogadandó halmazának a definíciója. Az enumerációs konstansoknak nincs memória allokálva, fordítás időben léteznek. – Értékek rendre: 0, 5, 2, 3, 4 enum {north, south, east, west}; – Default értékek felülírhatók: enum {north = 360, south = 180, west = 90, east = 15}; Létrehozható típus és változó is enumeráció alapján: enum Direction {north, south, west, east} mydir; – Létrehoz ey új Direction tipust – Ha csak ey példányt akarunk, nem kell típusnév, az objektum azonnal létrehozható (mydir). Ekkor természetesen nem kapunk típusnevet, ami a továbbiakban használható lenne újabb uyanilyen típusú objektum létrehozására. – Switch szerkezetben célszerű enumerációt használni: switch(d) { case north: .. break; case south: .. break; }
84 Rev 1.
9.3 A fájlkezelés alapjai A fájlműveletek a következő headereken keresztül érhetők el: ifstream – stream osztály olvasási műveletekhez ofstream – stream osztály írási műveletekhez – fstream – stream osztály irás/olvasás műveletekhez Használata: #include #include using namespace std; int main() { ofstream myOutFile(“hello.txt“); if( myOutFile.is_open()) { myOutFile << „Hello World“ << endl; myOutFile << „Hello World Again“ << endl; myOutFile.close(); } char buffer[100]; ifstream myInFile(“hello.txt“); if( myInFile.is_open()) { while( myInFile.getline(buffer, 100)) { // getline() a stream ref-t adja vissza, // vagy 0-t, ha vége cout << buffer << endl; } myInFile.close(); } else cout << „Cannot open input file“ << endl; }
85 Rev 1.
9.4 Típusdefiníció Létrehozhatók új, saját típusnevek. Typedef unsigned char int8_t; typedef char buffer[20]; Újabb jelölés: using unsigned char = int8_t; using char = buffer[20]; – Lényegében mindkét jelölés uyanaz, de vannak esetek, amikor csak a using használható (template-ek) – Igazi hasznuk akkor jelentkezik, amikor a típusdefiníció túl összetett lenne. Ilyenkor javítja az olvashatóságot. Typedef int (*fncPtr)(int) prinFunctionPointer;
86 Rev 1.
9.5 Dinamikus memória kezelés A programstacken kívül futásidőben igénybe vehető egy másik dinamikus memória terület, az u.n. heap is. A new operátor – – – –
Memória területet foglal, argumentum: típus visszatérő érték: pointer a lefoglalt memória területre. A visszatérő érték pointere az argumentumban adott típusra mutat. Int *intPtr = new int;
Vigyázat! – A lefoglalt memória nem az automatikus változók szabályai szerint működik! Void fnc(void) { char* text = new char[10]; } // fnc() végrehajtása után a text változó megszűnik, de a lefoglalt memória nem. A delete operátor – Felszabadítja a memória területet. Delete intPtr; // felszabadít egy objektumot delete [] text // felszabadít egy objektumokból álló vektort
87 Rev 1.
9.6 Tárolási osztályok és típusminősítők. Tárolási osztályok – auto int i; // Automatikus változó: a lokális változók élettartama korlátozott, ezért dinamikusan jönnek létre és pusztulnak el. Ritkán használjuk explicit módon, mert default a lokális változók esetében. – register int i; // Leyen ez a változó az élettartama alatt regiszterben, HA LEHETSÉGES! Explicit módon nem használjuk, mert az optimalizálók ezt csinálják. (Bármikor utólag is beírható a programba.) – static int func(void); // függvények esetén a forrásfile-ra szűkíti a hozzáférést. – static int i; // változók esetén meggátolja az automatikus kezelést. – extern int i; // globális változó bármelyik modulban definiálható. Extern esetén a fordító nem keresi az adott file-ban, hanem csak elteszi a hivatkozást, ami később lesz feloldva, ha minden eyéb előfordulás, és a definíció is megvan. – const - lásd később
88 Rev 1.
10 Egyéb osztálytulajdonságok
89 Rev 1.
10.1 Bináris operátor túlterhelésről részletesen Legyen egy osztályunk: class Element { public: Element(int value) : value(value) {} int getValue() const { return value; } private: int value; };
Bináris operátor túlterhelhető négy féle módon: 1. Különálló függvény hozzáféréssel a public adatagokhoz: Element operator+(const Element& a, const Element& b) { return Element(a.getValue() + b.getValue()); }
Az operandusok: e1 + e2 == operator+(e1, e2) 2. Tagfüggvényként: class Element { public: // Left operand is 'this'; right is 'other'. Element operator+(const Element& other) const { return Element(value + other.value); } // ... }; Az operandusok: e1 + e2 == e1.operator+(e2) 3. friend függvényként class Element { public: // Left operand is 'a'; right is 'b'. friend Element operator+(const Element& a, const Element& b) { return a.value + b.value; } // ... }; e1 + e2 == operator+(e1, e2) 4. Friend függvény, az osztályon kívül definiálva. U.az mint 3. class Element { public: friend Element operator+(const Element&, const Element&); // ... };
90 Rev 1.
Element operator+(const Element& a, const Element& b) { return a.value + b.value; } e1 + e2 == operator+(e1, e2)
91 Rev 1.
10.2 Konstans objektumváltozók Egy objektumváltozót lehet const-ként definiálni. Class Origo { const int xCoord; const int yCoord; //... } Viszont az inicializálás nem lehetséges deklaráláskor. Class Origo { const int xCoord = 0; // HIBÁS const int yCoord = 0; // HIBÁS //... } A magyarázat az, hogy mire az értékadás lefut, a változót a default konstruktor már létrhozza, de csak olvashatóként. Megoldás: Konstruktor és inicializáló lista, mert az előbb hajtódik végre, mint a konstruktor törzse. Class Origo { Origo(int x, int y): xCoord(x), yCoord(y); const int xCoord; const int yCoord; } Figyelem! Konstans objektumváltozóval nem lehet tömbméretet megadni, mivel csak futásidőben kap értéket.
92 Rev 1.
10.3 Konstans osztályváltozók Gyakorlati jelentősége jóval nagyobb. Olyan állandókat definiálhatunk vele, melyet az osztály biztosít szabályozot hozzáféréssel. Class Parser { public: static const char NEWLINE = '\n'; static const char C_RETURN = '\r'; } // hivatkozás: cout << „Hello World“ << Parser::NEWLINE; cout << „Again“ << pars.NEWLINE;
93 Rev 1.
10.4 Statikus tagfüggvények Akkor célszerű használni, ha egy osztályt példányosítás nélkül akarunk használni. Bár hozzáférhető az objektumon keresztül is az Obj.func() operátorral, helyes hozzáférés az osztályon keresztül: Class::func() class Point { //... static void Draw(); //... }
94 Rev 1.
10.5 Tagobjektumok Az osztályon belül tetszőleges típus felhasználható tagok definiálására. Így pl. másik oszályból képezet objektum is lehet tag. (Természetesen elhagyható, ha a tagobjektum nem vár inicializálást – pl. default argumentum.) class Rectangle { public: // konstruktor: Rectangle ( int left, int top, int right, int botom ); private: Point topLeft; Point botomRight; } // konstruktor definíció: // a tagobjektumokat is inicializálnia kell Rectangle::Rectangle (int left, int top, int right, int botom ) : topLeft(left, top), botomRight(right, botom) {}
95 Rev 1.
10.6 Objektum tömbök Ugyanúgy, mint az elemi típusoknál: Ha Point-nak létezik argumentum nélküli konstruktora: Point triangle[3]; Ha muszály paraméterezet konstruktort használni, akkor a tömböknél szokásos értékadással, de az értékek helyére a paraméterezet konstruktort kell beírni: Point triangle[3] = { Point(50, 50), Point(10, 20) }; Ha a konstruktornak csak egy paramétere van, akkor elegendő a paramétereket felsorolni: Set set[5] = {1, 2, 5, 6}; Objektum tömb dinamikus memóriafoglalás szintén lehetséges: Point *pentagon = new Point[5]; Mivel az objektum tömb futásidőben jön létre, a létrehozáskor nem lehet inicializálni. Ezért kell egy argumentum nélküli konstruktor! Ha emellet további inicializáció kell, akkor azt explicit módon kell végrehajtani! pentagon[0].Point(10, 20); Törléshez kell a [] !!! delete [] pentagon; // ha a [] elmarad, a destruktor csak az első elemre fut le!
96 Rev 1.
10.7 Osztály láthatóság Az osztály létrehoz egy blokkot, amin belülre korlátozza a láthatóságot. Minden tag az osztály láthatóságán belül van, Ha az osztályból egy globális szimbólumra akarunk kifelé hívatkozni, a :: -val tehető egyértelművé. Az osztály láthatósága lehet: ○ globális osztály: a legtöbb osztály C++_ban - erre valamennyi láthatósági körből lehet hívatkozni ○ beágyazot osztály: osztályon belül létrehozot osztály, ○ lokális osztály: blokkon belúl, vagy függvényen belül van létrehozva. A létrehozás módjának a megválasztásánál azt tartsuk szem előt, hogy milyen körben akarjuk az osztályt létrehozni! Célszerű mindíg a legszűkebb láthatóságot használni!
97 Rev 1.
10.8 Egyéb, fontosabb osztályok String osztály Az elemi char* típusú string helyet érdemes az összetet string osztályt használni. Példa: #include #include <string> using namespace std; int main() { string mystring1, mystring2, mystring3; mystring1 = “Hello “; mystring2 = “World“; mystring3 = mystring1 + mystring2; cout << mystring3 << endl; }
98 Rev 1.
11 Preprocesszor A GNU GCC által használt preprocesszor a GPP. Viszonylag jól használható a legtöbb nyelvhez, de vannak korlátai, ami miat
99 Rev 1.
11.1 Lépései: 4. Trigraph elemek behelyetesítése karakterekkel - nem minden implementációban. 5. Többsoros utasítások tördelésének megszüntetése. 6. Tokenizásció: a forrást preprocesszor tokenektre és wihtespace-ekre fordítja. Kommenteket wihitespace-ekkel helyetesíti. 7. Makro kifejtés és direktíva kezelés.
100 Rev 1.
11.2 Direktívák: File-ok beszúrása: #include <stdio.h> #include "ownheader.h" Behelyetesíti a direktívát a megadot headerfile tartalmával. Makro definíció: objektum makró: #define # define DEBUGLEVEL 3 függvény makró: #define (<paraméter lista>) #define RADTODEG(x) ((x) * 57.29) Zárójelezés kell, mert pl. a RADTODEG(r + 1) kifejtése zárójel nélkül: r + 1 * 57.29 lenne, ami miat a szorzás előbb lenne elvégezve. Vigyázzunk, hogy ne zárjuk le a sort ; - vel! Definíció feloldása: #undef Feltételes fordítás: #if #ifdef #ifndef #else #elif #endif Fordító specifikus direktíva: #pragma (<paraméter lista>)
101 Rev 1.