exception safety (tsz. exception safeties)
try
–catch
blokkokkal és throw
utasítással kivételeket használjunk – ezek megzavarhatják a normál vezérlésmenetet. Az exception safety garantálja, hogy a kivételek ellenére ne szivárogjon memória, ne romoljon el az objektum állapota, és a program biztonságosan folytatható vagy lezárható legyen.
C++-ban négy klasszikus szintet különböztetünk meg:
A művelet garantáltan nem dob kivételt.
swap()
függvények megvalósítása.std::vector::resize
, std::unique_ptr
void biztonsagos() noexcept {
// semmi nem dobhat
}
Az noexcept
kulcsszó C++11-től garantálja ezt.
Ha kivétel történik, az objektum eredeti állapota megmarad.
Példa:
std::vector<int> v = {1, 2, 3};
try {
v.insert(v.begin(), 42); // ha nem sikerül, v változatlan marad
} catch (...) {
// v nem sérült
}
A művelet után az objektum érvényes, de az állapota megváltozhat.
Példa:
void push(int x) {
data.push_back(x); // ha dob kivételt, az objektum akkor is használható
}
Kivétel esetén az objektum érvénytelen állapotba kerülhet.
void nemBiztonsagos(std::vector<int>& v) {
v.push_back(10); // lehet kivétel: memória!
v = 999; // csak akkor hajtódik végre, ha nem volt kivétel
}
Ha push_back()
kivételt dob, v = 999
már nem fut le → az állapot félbemaradt lehet.
void biztonsagos(std::vector<int>& v) {
std::vector<int> temp = v;
temp.push_back(10); // ha ez elbukik, v változatlan
temp = 999;
v.swap(temp);
}
std::unique_ptr<int> ptr(new int(42)); // automatikus felszabadítás
std::unique_ptr
, std::shared_ptr
kivételbiztonságot adnak.delete
-et, akkor is, ha kivétel történik.
noexcept
kulcsszó (C++11+)Azt jelzi a fordítónak és programozóknak, hogy egy függvény nem dob kivételt.
void torol() noexcept; // garantáltan nem dob kivételt
A std::swap
, move
műveletek noexcept
-ként működnek hatékonyan, különben a konténerek például másolni fognak move helyett.
std::vector::push_back
std::vector::resize
T
konstruktor kivételmentes → strong guarantee
Az operátorokat, mint operator=
, operator+
, érdemes kivételbiztosan implementálni:
MyClass& operator=(const MyClass& other) {
if (this != &other) {
MyClass temp(other);
swap(temp); // strong guarantee
}
return *this;
}
Tipp | Miért hasznos |
---|---|
Használj try/catch -et csak magas szinten
|
Ne tömd tele kivételkezeléssel a kis függvényeket |
RAII mindenre | Erőforrás- és állapotkezelés automatikusan |
Kerüld a kivételeket destruktorban | Előfordulhat, hogy másik kivétel már aktív |
Preferáld noexcept -et
|
Gyorsabb és megbízhatóbb |
Használj másolás/move-and-swap idiomát | Erős garanciához kiváló |
Kritikus helyzet!
Ha egy kivétel történik egy másik kivétel közben (pl. destruktorban), a program terminate()-et hív → leállás.
Ezért:
~MyClass() noexcept {
try {
// műveletek
} catch (...) {
// csendben elnyeli a hibát
}
}
Fogalom | Leírás |
---|---|
Exception safety | Biztonságos viselkedés kivételek esetén |
No-throw | Semmilyen kivételt nem dob |
Strong | Vagy sikerül, vagy nem változik semmi |
Basic | Érvényes, de módosulhat az állapot |
No guarantee | Semmit nem garantál |
Eszközök | RAII, smart pointer, swap idiom, noexcept
|
STL viselkedés | Jellemzően legalább basic guarantee |
A kivételbiztonság nemcsak „extra védelem”, hanem alapfeltétele a megbízható programoknak. A modern C++ lehetővé teszi, hogy erős garanciákat adjunk, és a helyes erőforrás-kezeléssel és noexcept
-tel stabil és hatékony kódot írjunk – még akkor is, ha a dolgok félremennek.