Névterek, memóriakezelés
Szeberényi Imre, Somogyi Péter BME IIT szebi@iit.bme.hu
const
, bool
, inline
, template
) √ …I/O stream √ …
Dinamikus memória kez. nyelvi szint. (new
, delete
)
::
), vagy a using namespace
direktívával hivatkozhatunk.namespace nevterem {
int alma;
float fv(int i);
char *nev;
} // namespace nevterem
void a() {
nevterem::alma = 12;
float f = nevterem::fv(5);
}
using namespace nevterem;
void b() {
alma = 8;
float f = fv(3);
}
A using namespace
direktívával a teljes névteret:
vagy annak egy részét láthatóvá tehetjük:
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.
namespace { // nincs neve
void solveTheProblem() { /* ... */ }
// ...
} // névtér vége
int main() {
solveTheProblem();
// ...
}
A névterek egymásba ágyazhatók:
Létező névterhez újabb név rendelése (rövidítés):
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;
} // namespace N
int main() {
fx(N::origo); // N-ben keres
N::fy(4);
}
Az egyszerű példákban kinyitjuk az egész névteret az egyszerűbb írásmód miatt:
Header-be pedig soha ne tegyük! Miért?
#include <iostream>
using std::cout;
using std::endl;
int main() {
cout << "Hello C++\n";
//-> std::operator<<(std::cout, "Hello C++\n");
// fv, overload + ADL miatt ^
int i;
std::cin >> i;
cout << "2 * " << i << " = " << 2 * i << endl;
}
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;
// ...
operator<<
egy ostream
típushoz (insert operátor)operator>>
egy istream típushoz (extract operátor)A működés bonyolult. Most csak a felszínt kapargatjuk.
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ó
Hatás szempontjából 3 fajtájuk van:
Azonnali hatás:
csak a következő kiírásig érvénes
a stream állapota tartósan megváltozik
https://infocpp.iit.bme.hu/iomanip
Úgy viselkedik, mint a függvény, de beépül.
OK, de kell double
-re, long
-ra, …
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); }
Nyelvi elem
template <typename T> // korábban class volt a typename
// ^ formális sablonparaméter
inline T max(T a, T b) {
return a < b ? b : a;
} // hatókör: a template kulcsszót követő dekl./def. vége
#include <iostream>
using namespace std;
int main() {
cout << ::max<long>(2, 10);
// ^ aktuális sablonparaméter
cout << ::max<double>(2.5, 3.14);
cout << ::max(40, 50);
// ^ ^ levezethető a paraméterekből
}
template <typename T> T max(T a, T b) {
// inline felesleges, mert...
return a < b ? b : a;
}
#include <iostream>
using namespace std;
int main() {
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:
Összehasonlító fv.:
template <typename T> T max(T a, T b) { return a < b ? b : a; }
// max 3 paraméteres változata (overload)
template <typename T, typename C> T max(T a, T b, C cmp) {
return cmp(a, b) ? b : a; // predikátum ^
}
#include <cstring>
bool strLess(const char *s1, const char *s2) {
return strcmp(s1, s2) < 0;
}
#include <iostream>
int main() {
std::cout << max(40, 50); // 50
std::cout << max("alma", "korte", strLess); // korte
}
Template specializáció:
template <typename T> T max(T a, T b) { return a < b ? b : a; }
// Teljes specializáció T::= const char* esetre
#include <cstring>
template <>
inline const char *max(const char *a, const char *b) {
return strcmp(a, b) > 0 ? a : b;
}
#include <iostream>
int main() {
std::cout << max<long>(2, 10) << std::endl; // 10
std::cout << max<double>(1, 3.14) << std::endl; // 3.14
std::cout << max(40, 50) << std::endl; // 50
std::cout << max("alma", "korte") << std::endl; // korte
std::cout << max("Ádám", "Béla") << std::endl; // Ádám ??
}
Egy betű → egy karakter kódolásnál strcoll()
fv.
#include <cstring>
template <typename T> T max(T a, T b) { return a < b ? b : a; }
template <> const char *max(const char *a, const char *b) {
return strcoll(a, b) < 0 ? b : a;
}
#include <iostream>
int main() {
setlocale(LC_ALL, "");
std::cout << max("Ádám", "Béla") << std::endl; // Béla
}
https://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.
inline
-ként fordulhat.template <typename T, typename P>
T legElem(T a[], int n, P pred) {
T tmp = a[0];
for (int i = 1; i < n; ++i) {
if (pred(a[i], tmp)) {
tmp = a[i];
}
}
return tmp;
}
pred
predikátum függvény adja meg.// template <typename T, typename P>
// T legElem(T a[], int n, P pred) { /* ... */ }
// a predikátum is lehet template
template <typename T> bool nagyobb_e(T a, T b) {
return a > b;
}
#include <iostream>
int main() {
int tomb[] = {1, 3, 4, 80, -21};
std::cout << legElem(tomb, 5, nagyobb_e<int>);
}
https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_02/ → predicate
C:
CPP:
malloc()
, free()
, realloc()
new
, delete
, new[]
, delete[]
delete[]
operátorral szabadítsuk fel.realloc()
-nak megfelelő.Számokat olvasunk be és fordított sorrendben kiírjuk. Láncban tárolunk.
#include <iostream>
#include "szimpla_lanc.hpp"
using std::cout;
using std::cin;
int main() {
int i;
Lanc_elem* kezdo = NULL; // üres lánc
while ((cin >> i) != NULL) { kezdo = lanc_epit(kezdo, i); }
cout << "Adatok fordított sorrendben:" << std::endl;
lanc_kiir(cout, kezdo);
lanc_felszabadit(kezdo);
}
#ifndef SZIMPLA_LANC_HPP
#define SZIMPLA_LANC_HPP
#include <iostream>
struct Lanc_elem { // Struktúra név típussá vált
int adat;
Lanc_elem *kov;
};
Lanc_elem *lanc_epit(Lanc_elem *p, int i);
void lanc_kiir(std::ostream &os, const Lanc_elem *p);
// ^ Névteret nem nyitunk .h-ban!
void lanc_felszabadit(Lanc_elem *p);
#endif // SZIMPLA_LANC_HPP
#include "szimpla_lanc.hpp"
Lanc_elem *lanc_epit(Lanc_elem *p, int a) {
Lanc_elem *uj = new Lanc_elem;
// ^ Vigyázat ez operátor!
// Nem szabad a malloc-ot formálisan lecserélni
// mert mást jelent!
uj->adat = a;
uj->kov = p;
return uj;
}
void lanc_kiir(std::ostream &os, const Lanc_elem *p) {
while (p != NULL) {
os << p->adat << ' ';
p = p->kov;
}
}
void lanc_felszabadit(Lanc_elem *p) {
while (p != NULL) {
Lanc_elem *tmp = p->kov;
delete p;
p = tmp;
}
}
void lanc_kiir(std::ostream &os, const Lanc_elem *p) {
while (p != NULL) {
os << p->adat << ' ';
p = p->kov;
}
}
void lanc_felszabadit(Lanc_elem *p) {
while (p != NULL) {
Lanc_elem *tmp = p->kov;
delete p;
p = tmp;
}
}
https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_02/ → fordít
new_handler
, ha van, egyébkéntbad_alloc
kivétel generálódikNULL
pointerrel tér vissza#include <cstdlib>
#include <iostream>
void outOfMem() {
std::cerr << "Gáz van\n";
// Ha visszatérne, akkor a new ciklusban hívná.
exit(1);
}
int main() {
std::set_new_handler(outOfMem);
double *p = new double;
delete p;
}
printf
függvényt nem a visszatéré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.:
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ó.
try
)throw
)catch
)#include <stdexcept>
// ...
valami;
try {
// ... Kritikus művelet1 pl. egy függvény hívása, aminek a
// belsejében:
if (hiba) throw kifejezes_tipus1("Légy");
// ... Kritikus művelet2 pl. egy másik függvény hívása,
// aminek a belsejében:
if (hiba) throw kifejezes_tipus2("leves");
} catch (tipus1& param) {
// ... Kivételkezelés1
} catch (tipus2& 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.
#include <stdexcept>
double osztas(int y) {
if (y == 0) { // Hiba/kivétel észlelése
throw "Osztás nullával";
// ^ A kivételt azonosító érték eldobása.
}
return (5.0 / y);
}
#include <iostream>
using namespace std;
int main() {
try { // Felügyelt szakasz. Ennek a működése
// során fordulhat elő a kivételes eset.
cout << "5/2 =" << osztas(2) << endl;
cout << "5/0 =" << osztas(0) << endl;
// Kivétel elkapása és kezelése:
} catch (const char *p) { // Típus azonosít (köt össze).
cout << p << endl;
}
}
#include <iostream>
using namespace std;
int main() {
long db = 0;
try { // A kivételes eset itt keletkezhet
while (true) {
double *p = new double[1022];
db++; // ^ kivételes eset lehet
}
// Kivétel elkapása és kezelése:
} catch (std::bad_alloc &) {
cerr << "Gaz van" << std::endl;
} // TODO: delete[] p !!!
cerr << "Ennyi new sikerult:" << db << endl;
}
ural2:~$ cp ~szebi/proga2/mem_alloc.cpp .
ural2:~$ g++ -static mem_alloc.cpp -o mem_alloc
ural2:~$ ( ulimit -d 24; ./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.
Az ural2-n levő kóddal a new_handler működése is tesztelhető. Ehhez a fordításkor definiálni kell a HANDLER
azonosítót:
ural2:~$ g++ -static mem_alloc.cpp –DHANDLER -o mem_alloc
ural2:~$ ( ulimit -d 24; ./mem_alloc )