2. előadás

Tipp: a diák között lépkedni a J és K billentyúkkel lehet. Szöveges kereséshez ctrl-F használható.

Letöltés

1.

Programozás alapjai 2. (2. ea) C++ névterek, memóriakezelés Szeberényi Imre, Somogyi Péter BME IIT <szebi@iit.bme.hu> M Ű E GY E T E M 1 7 82 C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 1-

2.

Hol tartunk? • Változó definíció bárhol √ • Struktúranév típussá válik √ • Korábban csak preprocesszorral megoldható dolgok nyelvi szintre (const, bool, inline, template) √ ... • Kötelező a prototípus használata √ • Referencia, cím szerinti paraméterátadás √ • Többarcú fv.-ek (polimorf, túlterhelés, overload) √ ... • Alapértelmezésű (default) argumentumok √ • I/O stream √ ... • Névterek • Dinamikus memória kez. nyelvi szint. (new, delete) C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 2-

3.

Névterek, scope operátor • A moduláris programozás támogatására külön névterületeket definiálhatunk. • Ez ebben levő nevekre (azonosítókra) a hatókör (scope) operátorral (::), vagy a using namespace direktívával hivatkozhatunk. namespace nevterem { int alma; float fv(int i); char *nev; } C++ programozási nyelv © BME-IIT Sz.I. nevterem::alma = 12; float f = nevterem::fv(5); using namespace nevterem; alma = 8; float f = fv(3); 2021.02.15. - 3-

4.

using direktíva A using namespace direktívával a teljes névteret, vagy annak egy részét láthatóvá tehetjük: using namespace nevterem; alma = 8; float f = fv(3); using nevterem::alma; using nevterem::fv; alma = 8; float f = fv(3); nevterem::nev = "Dr. Bubo"; C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 4-

5.

Név nélküli névtér Biztosítani akarjuk, hogy egy kódrészlet csak az adott fordítási egységből legyen elérhető. Névütközés biztosan nem lesz. #include <iostream> namespace { // nincs neve void solveTheProblem() { std::cout << "Solved\n";} } // névtér vége int main() { solveTheProblem(); } C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 5-

6.

Névterek egymásba ágyazása, alias • A névterek egymásba ágyazhatók. • Egy létező névterhez egy újabb nevet rendelhetünk (rövidítés). namespace kis_nevterem { namespace belso_terem { int fontos; } } namespace bent = ::kis_nevterem::belso_terem; bent::fontos = 8; C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 6-

7.

Argument-dependent lookup (ADL) Nem minősített függvények hívásakor a fv. argumentumainak névterében is keres. namespace N { struct P { int x, y;}; void fx(P p) { ... } void fy(int i) { ... } P origo; } C++ programozási nyelv © BME-IIT Sz.I. N-ben keres int main() { fx(N::origo); N::fy(4); } 2021.02.15. - 7-

8.

Az std névtér • Standard függvények konstansok és objektumok névtere. Ebben van standard az I/O is. • Az egyszerű példákban kinyitjuk az egész névteret az egyszerűbb írásmód miatt: using namespace std; • Komoly programokban ez nem célszerű. • Header-be pedig soha ne tegyük! Miért? C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 8-

9.

Standard I/O madártávlatból #include <iostream> using std::cout; using std::endl; fv, overload + ADL miatt int main() { cout << ″Hello C++\n″; // std::operator<<(std::cout, "Hello C++\n"); int i; std::cin >> i cout << ″2 * ” << i ″ = ″ << 2 * i << endl; } C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 9-

10.

<iostream> Standard I/O objektumait definiáló fejléc fájl valahogy így néz ki: #include <ios> #include <streambuf> #include <istream> #include <ostream> namespace std { extern istream cin; extern ostream cout; extern ostream cerr; extern ostream clog; .... Kiírás: operator<< egy ostream típushoz (insert) Beolvasás: operator>> egy istream típushoz (extract) A működés bonyolult. Most csak a felszínt kapargatjuk. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 10 -

11.

<iostream> /2 • Az iostream kapcsán megismert eszközök a fájlkezelésnél is használhatók. • A stream típus logikai környezetben igazzá, vagy hamissá konvertálódik attól függően, hogy hibás állapotban van-e a stream. • A streamek működése, belső állapota többek között manipulátorokkal befolyásolható C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 11 -

12.

<iostream> /3 #include <iostream> #include <iomanip> using namespace std; int main() { cout << "Hexa konverer:" << endl; int x; logikai környezet. while (cin >> x) cout << hex << x << endl; } manipulátorok C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 12 -

13.

Manipulatorok <iomanip> Hatás szempontjából 3 fajtájuk van: • Azonnali hatás: cout << endl; // kiír egy ‚’\n’-t és üríti a buffert cin >> ws; // eldobja az összes whitespcace-t • csak a következő kiírásig érvénes cout << setw(4) << 3 << 4; // ”˻˻˻34” • a stream állapota tartósan megváltozik cout << setfill(’0’) << setw(3) << 7 // ”007” https://infocpp.iit.bme.hu/iomanip C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 13 -

14.

Inline függvények (ism.) #define max(a,b) (a) < (b) ? b : a x = 8, y = 1; z = max(x++, y++); x,y,z = ? inline int max(int a, int b) { return(a < b ? b : a); } Úgy viselkedik, mint a függvény, de beépül. OK, de kell double-re, long-ra, ... C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 14 -

15.

1. megoldási próbálkozás inline int max(int a, int b) { return(a < b ? b : a); } inline long max(long a, long b) { return(a < b ? b : a); } inline double max(double a, double b) { return(a < b ? b : a); } Az azonos nevekből nincs baj (túlterhelés), de fárasztó leírni minden típushoz. Lehet, hogy a makró mégis jobb? C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 15 -

16.

Megoldás: template – nyelvi elem formális sablonparaméter template <typename T> //korábban class typename inline T max(T a, T b) { return a < b ? b : a; } hatókör: a template kulcsszót követő deklaráció/definíció vége aktuális sablonparaméter cout << max<long>(2, 10); cout << max<double>(2.5, 3.14); cout << max(40, 50); C++ programozási nyelv © BME-IIT Sz.I. levezethető a paraméterekből 2021.02.15. - 16 -

17.

Mi a sablon? • Parametrikus polimorfizmus. • Típus biztos nyelvi elem az általánosításhoz. • Gyártási forma: a sablonparaméterektől függően példányosítja a fordító (megírja a programot). • Paraméter: típus, konstans, függvény, sablon • Feldolgozása fordítási idejű ezért a példányosítás helyének és a sablonnak egy fordítási egységben kell lennie. gyakran header fájlba tesszük • A példában függvénysablont láttunk, de később adatszerkezeteknél is használni fogjuk. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 17 -

18.

Mindeden típusra jó ez a max? template <typename T> T max(T a, T b) { // inline felesleges, mert... return a < b ? b : a; } cout << max(40, 50); cout << max(”alma”, ”korte”) // ”alma” const char * Valóban a címeket akartuk összehasonlítani? strcmp kellene, de hogyan? Több megoldás van: 1. Összehasonlító függvény (predikátum) 2. Specializáció 3. ... C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 18 -

19.

1. megoldás: összehasonlító fv. // max 3 paraméteres változata (overload) template <typename T, typename C> T max(T a, T b, C cmp) { Predikátum return cmp(a, b) ? b : a; } bool strLess(const char *s1, const char *s2) { return strcmp(s1, s2) < 0; } cout << max(40, 50); // 50 cout << max("alma", "korte", strLess); // korte C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 19 -

20.

2. megoldás: Template specializáció // Teljes specilaizáció T::= const char* esetre template<> inline const char* max(const char* a, const char* b) { return strcmp(a,b) > 0 ? a : b; } cout cout cout cout cout << << << << << max<long>(2, 10); max<double>(1, 3.14); max(40, 50); max("alma", "korte"); max("Ádám", "Béla"); C++ programozási nyelv © BME-IIT Sz.I. // // // // // 10 3.14 50 korte Ádám ?? 2021.02.15. - 20 -

21.

Ékezettel C-ben is baj volt Egy betű egy karakter kódolásnál srcoll() fv. template<> const char* max(const char* a, const char* b) { return strcoll(a,b) < 0 ? b : a; } cout << max("Ádám", "Béla") << endl; // Béla git.ik.bme.hu/Prog2/eloadas_peldak/ea_02/ -> template Egyéb kódolásnál (pl. UTF-8) a C string helyett valami mást érdemes használni, de majd később. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 21 -

22.

Függvénysablon összefoglalás • Függvények, algoritmusok általánosítási eszköze. • Hatékony, paraméterezhető, újrafelhasználható, általános. • Fordítási időben generálódó függvény. • A példányosítás helyének és a sablonnak egy fordítási egységben kell lennie. • Automatikusan inline-ként fordulhat. • Függvény overload, default paraméterek ugyanúgy mint más függvényekkel. • Sablon specializáció paraméter típustól függően más kód generálódik. Generikus programozás. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 22 -

23.

Predikátum • Logikai függvény, ami egy algoritmus működését befolyásolja • Pl.: válasszuk ki egy tömbből a leg... elemet! • Melyik a leg? A predikátum függvény adja meg. template <typename T, typename S> T legElem(T a[], int n, S sel) { T tmp = a[0]; for (int i = 1; i < n; ++i) if (sel(a[i], tmp)) tmp = a[i]; return tmp; } C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 23 -

24.

Predikátum példa template <typename T, typename S> T legElem ... // a predikátum is lehet template template <typename T> bool nagyobb_e(T a, T b) { return a > b; } int tomb[] = { 1, 3, 4, 80, -21 }; cout << legElem(tomb, 5, nagyobb_e<int>); git.ik.bme.hu/Prog2/eloadas_peldak/ea_02/ -> predicate C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 24 -

25.

Dinamikus memória C: #include <malloc.h> .... struct Lanc *p; p = malloc(sizeof(Lanc)); if (p == NULL) .... free( p ); C++ programozási nyelv © BME-IIT Sz.I. C++: Lanc *p; p = new Lanc; .... delete p; Tömb: int *p; p = new int[10]; delete[] p; 2021.02.15. - 25 -

26.

Dinamikus memória /2 C: malloc(), free(), realloc() • C++-ban is használható de csak nagyon körültekintően, ugyanis nem hívódik meg a megfelelő konstruktor ill. destruktor. Ezért inkább ne is használjuk. C++ (operátor): new, delete, new[], delete[] • Figyeljünk oda, hogy a tömböket mindig a delete[] operátorral szabadítsuk fel. C++: nincs realloc()-nak megfelelő. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 26 -

27.

Példa: fordit_main.cpp #include <iostream> #include "szimpla_lanc.h" using std::cout; Számokat olvasunk be és using std::cin; fordított sorrendben kiírjuk. int main() { Láncban tárolunk. int i; Lanc_elem* kezdo = NULL; // üres lánc while (cin >> i) kezdo = lanc_epit(kezdo, i); cout << "Adatok forditott sorrendben:" << std::endl; lanc_kiir(cout, kezdo); lanc_felszabadit(kezdo); C++}programozási nyelv © BME-IIT Sz.I. - 27 2021.02.15.

28.

Példa: szimpla_lanc.h #ifndef SZIMPLA_LANC_H Struktúra név #define SZIMPLA_LANC_H típussá vált #include <iostream> struct Lanc_elem { int adat; Névteret nem nyitunk .h-ban! Lanc_elem* kov; }; Lanc_elem* lanc_epit(Lanc_elem* p, int i); void lanc_kiir(std::ostream& os, const Lanc_elem* p); void lanc_felszabadit(Lanc_elem* p); #endif // SZIMPLA_LANC_H C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 28 -

29.

Példa: szimpla_lanc.cpp #include "szimpla_lanc.h" Lanc_elem* lanc_epit(Lanc_elem* p, int a) { Lanc_elem *uj = new Lanc_elem; uj->adat = a; uj->kov = p; Vigyázat ez operátor! return uj; Nem szabad a malloc-ot formálisan lecserélni, mert } mást jelent! C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 29 -

30.

Példa: szimpla_lanc.cpp /2 ... void lanc_kiir(std::ostream& os, const Lanc_elem* p) { while (p != NULL) { os << p->adat << ' '; p = p->kov; } } C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 30 -

31.

Példa: szimpla_lanc.cpp /3 ... void lanc_felszabadit(Lanc_elem *p) { while (p != NULL) { Lanc_elem *tmp = p->kov; delete p; p = tmp; } } git.ik.bme.hu/Prog2/eloadas_peldak/ea_02/ -> fordít C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 31 -

32.

Mi van, ha elfogy a memória ? Két működési mód választható: 1. Hibakezelő mechanizmus indul be – meghívódik a new_handler, ha van, egyébként – bad_alloc kivétel generálódik 2. NULL pointerrel tér vissza Mai implementációkban az alapeset a kivételkezelés, ami manapság a hibakezelés szabványosnak tekinthető megoldása. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 32 -

33.

new_handler Kevésbé használt megoldás, de pl. garbage colletor-hoz használható. void outOfMem() { cerr << "Gáz van\n"; exit(1); A visszatérésnek (return) akkor van értelme, ha ez a rutin képes területet } felszabadítani. Különben végtelen ciklus. int main() { set_new_handler(outOfMem); double p* = new double; .... C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 33 -

34.

Kivételes esetek • Gyakran hibakezelésnek mondjuk, de nem csak hiba lehet kivételes eset. Kivételes eset pl. amikor egy program először indul el az adott környezetben, és a további működéséhez ... • Alapvetően két kategóriába sorolhatók: – végzetes, – nem végzetes • Kezelésük nagyon különböző – – – – nincs mit tenni, meg kell állni az eset ott helyben kezelhető az eset a program más részében kezelhető nem tudjuk, elhalasztjuk (másra bízzuk) a döntést. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 34 -

35.

Példák kivételes esetre • printf függvényt nem a vssszatrési értéke miatt hívjuk, pedig a visszatérési értékében jelzi, hogy sikerült-e kiírni. Mit kell/lehet tenni? Pl: assert(printf(”Hello Cicus”) >= 0); • Egy erőforrás lefoglalása nem sikerül Mit kell/lehet tenni? Meg lehet próbálni később C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 35 -

36.

Példák kivételes esetre /2 • Adott nyelvhez (természetes) tartózó üzenetek állománya nem található. Mit kell/lehet tenni? Pl: Meg lehet próbálni másik nyelvet • Másodfokú egyenletnek nincs valós megoldása. Mit kell/lehet tenni? Jelezni kell a függvényt hívónak C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 36 -

37.

Kivételes esetek kezelése, jelzése • Kinek kell jelezni? – felhasználó, másik programozó, másik program – saját magunknak • A kivételes eset kezelése gyakran nem annak keletkezési helyén történik. (Legtöbbször nem tudjuk, hogy mit kell tenni. Megállni, kiírni valami csúnyát, stb.) C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 37 -

38.

Kivétel kezelés • C++ típus orientált kivételkezelést támogat, amivel a kivételes esetek kezelésének szinte minden formája megvalósítható. • A kivételkezeléshez tartozó tevékenységek: – figyelendő kódrészlet kijelölése (try) – kivétel továbbítása (throw) – esemény lekezelése (catch) C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 38 -

39.

Kivételkezelés = globális goto Kivételes eset: throw A Fv. try Felügyelt műveletek beleértve az innen hívott függvényeket is. catch Kivétel elkapása, típus alapján, és kezelése. Kivételes eset: throw B C: setjmp( ) longjmp( ) C++ programozási nyelv © BME-IIT Sz.I. További műv. A: A típusú érték B: B típusú érték 2021.02.15. - 39 -

40.

Kivételkezelés/2 try { ..... Kritikus művelet1 pl. egy függvény hívása, aminek a belsejében: if (hiba) throw kifejezés_tipus1; ..... Kritikus művelet2 pl. egy másik függvény hívása, aminek a belsejében: if (hiba) throw kifejezés_tipus2; } catch (típus1 param) { ..... Kivételkezelés1 } catch (típus2 param) { ..... Kivételkezelés2 } A hiba tetszőleges mélységben keletkezhet. Közvetlenül a try-catch blokkban a throw-nak nincs sok értelme, hiszen akkor már kezelni is tudnánk a hibát. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 40 -

41.

Kivételkezelés példa Hiba/kivétel észlelése Felügyelt szakasz. Ennek a működése során fordulhat elő a kivételes eset. double osztas(int y) { if (y == 0) throw "Osztas nullaval"; return((5.0/y); } A kivételt azonosító érték eldobása. Típus azonosít (köt össze). int main() { try { cout << "5/2 =" << osztas(2) << endl; cout << "5/0 =" << osztas(0) << endl; } catch (const char *p) { cout << p << endl; Kivétel elkapása és } kezelése. } C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 41 -

42.

Kivételkezelés a memóriára #include <iostream> using std::cerr; A kivételes eset int main() { itt keletkezhet. long db = 0; try { while(true) { double *p = new double[1022]; db++; } } catch (std::bad_alloc) { Kivétel elkapása cerr << "Gaz van" << endl; és kezelése. // Fel kellene szabadítani, de .... ? } cerr << "Ennyi new sikerult:" << db << endl; } C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 42 -

43.

Futtatás egy Linux VM-ben ~$ git clone \ https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_02 ~$ cd ea_02/mem_alloc ~$ g++ -static mem_alloc.cpp -o mem_alloc ~$ ( ulimit -d 500; ./mem_alloc ) # A –static azért kell, hogy a betöltésnél ne legyen szükség # extra loader-re, ami extra memóriát használ. # A zárójel azért kell, hogy új shell induljon. # A sorvégi \ folytatósort jelöl. C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 43 -

44.

Futtatás egy Linux VM-ben /2 Az kóddal a new_handler működése is tesztelhető. Ehhez a fordításkor definiálni kell a HANDLER azonosítót: ~$ g++ -static mem_alloc.cpp \ –DHANDLER -o mem_alloc ~$ ( ulimit -d 500; ./mem_alloc ) C++ programozási nyelv © BME-IIT Sz.I. 2021.02.15. - 44 -

Utolsó frissítés: 2021-02-13 01.27