Ce face throw
throw expresie; creează un obiect excepție și pornește căutarea unui catch potrivit, urcând prin funcțiile care au apelat-o. Poți arunca orice tip, dar convenția este să arunci obiecte derivate din std::exception: std::runtime_error, std::invalid_argument, std::out_of_range (din <stdexcept>) sau clase proprii derivate din ele.
Într-un bloc catch, throw; fără nimic după aruncă din nou aceeași excepție, mai departe, după ce ai făcut ceva cu ea (de exemplu ai scris-o într-un jurnal).
throw este și o expresie, deci poate apărea într-un operator ternar: return b != 0 ? a / b : throw std::domain_error("împărțire la zero");.
Exemple
Exemplul 1: Validare cu throw
#include <print>
#include <stdexcept>
double retrage(double sold, double suma) {
if (suma <= 0) {
throw std::invalid_argument("suma trebuie să fie pozitivă");
}
if (suma > sold) {
throw std::runtime_error("fonduri insuficiente");
}
return sold - suma;
}
int main() {
for (double suma : {100.0, -5.0, 900.0}) {
try {
std::println("Sold nou: {}", retrage(500, suma));
} catch (const std::exception& e) {
std::println("Eroare: {}", e.what());
}
}
}
Rezultat
Sold nou: 400
Eroare: suma trebuie să fie pozitivă
Eroare: fonduri insuficiente
Exemplul 2: Excepție proprie
Clasa derivă din std::runtime_error, deci primește what() și poate fi prinsă ca std::exception; în plus, ține codul produsului.
#include <print>
#include <stdexcept>
#include <string>
class StocEpuizat : public std::runtime_error {
public:
explicit StocEpuizat(const std::string& cod)
: std::runtime_error("stoc epuizat pentru " + cod), cod_(cod) {}
const std::string& cod() const { return cod_; }
private:
std::string cod_;
};
void comanda(const std::string& cod, int stoc) {
if (stoc == 0) throw StocEpuizat(cod);
std::println("Comandă plasată: {}", cod);
}
int main() {
try {
comanda("LAP-15", 3);
comanda("TEL-07", 0);
} catch (const StocEpuizat& e) {
std::println("{} (cod: {})", e.what(), e.cod());
}
}
Rezultat
Comandă plasată: LAP-15
stoc epuizat pentru TEL-07 (cod: TEL-07)
Exemplul 3: Aruncare din nou cu throw;
Funcția notează eroarea și o trimite mai departe, neschimbată; tratarea finală se face în main.
#include <print>
#include <stdexcept>
void proceseaza() {
try {
throw std::runtime_error("conexiune pierdută");
} catch (const std::exception& e) {
std::println("[jurnal] {}", e.what());
throw; // aceeași excepție, mai departe
}
}
int main() {
try {
proceseaza();
} catch (const std::runtime_error& e) {
std::println("[main] reîncerc mai târziu: {}", e.what());
}
}
Rezultat
[jurnal] conexiune pierdută
[main] reîncerc mai târziu: conexiune pierdută
Greșeli frecvente
- Scrii
throw e;în loc dethrow;încatch. Se aruncă o copie de tipul declarat încatch, deci o excepție derivată pierde tipul real. - Arunci o excepție dintr-un destructor. Dacă destructorul rulează deja din cauza altei excepții, programul se oprește cu
std::terminate; în plus, destructorii sunt implicitnoexcept. - Arunci pointeri (
throw new MyError()). Cine prinde trebuie să își amintească să facădelete; aruncă obiecte prin valoare.
Comenzi înrudite
try, catch: try marchează codul care poate arunca o excepție, iar blocurile catch prind și tratează excepțiile de un anumit tip.noexcept: Marchează o funcție care promite că nu aruncă excepții; ca operator, verifică la compilare dacă o expresie poate arunca.class: Definește un tip nou care grupează date (membri) și funcțiile care lucrează cu ele (metode); baza programării orientate pe obiecte.std::exception: Clasa de bază a tuturor excepțiilor din biblioteca standard; what() returnează mesajul erorii.std::runtime_error: Excepție pentru erorile care apar doar la rulare, din cauze externe: fișier lipsă, rețea, date greșite.