2. előadás
Letöltés
- Példaprogramok
- Nyomtatáshoz, 3 dia/lap (198K)
- Nyomtatáshoz, 6 dia/lap (195K)
- Képernyőre, 1 dia/lap (színes) (187K)
- E-könyv olvasóhoz, 1 dia/lap (199K)
- HTML (generalt)
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 -