ProgrammatieC++ Backend Ontwikkelaar

Hoe werkt het exceptionsysteem in C++ en hoe verschilt het van andere talen? Hoe ontwerp je exceptionafhandeling in industriële toepassingen?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Het exceptionsysteem in C++ is gebaseerd op het try-catch mechanisme en het sleutelwoord throw. Bij het optreden van een uitzonderlijke situatie (bijvoorbeeld een resourcefout, schending van een invariant), wordt binnen het try blok een uitzondering geactiveerd via de throw operator. De zoektocht naar de geschikte handler (catch) gebeurt door de stack te doorlopen tot de eerste overeenkomstige type.

Belangrijke details:

  • In tegenstelling tot Java of C#, kunnen uitzonderingen in C++ van elk type zijn (meestal een klasse die afgeleid is van std::exception).
  • In C++ is er geen verplichte specificatie voor uitzonderingen (zoals checked exceptions in Java), daarin is in C++11 en later de noexcept specificator geïntroduceerd.
  • Bij het gooien van een uitzondering worden destructors aangeroepen voor alle objecten op de stack tussen het punt van gooien en het punt van afhandeling — het is belangrijk om RAII te ondersteunen.

Ontwerp:

  • Gooi alleen uitzonderingen voor "onverwachte" fouten, voor de rest gebruik je een return code.
  • Vang op met een constante referentie — catch(const std::exception& e).
  • GNU beveelt aan om objecten te gooien die gekopieerd kunnen worden, en geen pointers (vermijd heap-geheugen).

Voorbeeldcode:

#include <iostream> #include <stdexcept> void mayFail(bool fail) { if (fail) throw std::runtime_error("Procesfout"); } int main() { try { mayFail(true); } catch (const std::exception& ex) { std::cout << "Vang exception: " << ex.what() << std::endl; } }

Vraag met een valstrik

Wat gebeurt er als een uitzondering wordt gegooid vanuit een destructor terwijl een andere uitzondering omhoog wordt doorgegeven in de stack?

Antwoord: Dit zal leiden tot een noodstop van het programma via std::terminate(), omdat het tijdens de afhandeling van een uitzondering niet is toegestaan om een nieuwe uitzondering te genereren (double exception), anders wordt de stack unwinding verstoord.

Voorbeeld:

struct CrashOnDestruct { ~CrashOnDestruct() noexcept(false) { throw std::runtime_error("Fout in destructor!"); } }; void func() { CrashOnDestruct obj; throw std::logic_error("Foutafhandeling"); } int main() { try { func(); } catch (...) { } } // Zal eindigen via std::terminate()

Voorbeelden van echte fouten door onbekendheid met de nuances van het onderwerp


Verhaal

Binnen een grote financiële applicatie werden uitzonderingen gegooid vanuit de destructor van een wrapperklasse voor de database. Een van de belangrijke transacties activeerde een uitzondering bij een fout, en bij het unwinden werd de destructor aangeroepen, die ook een fout gooide. Hele applicatie kwam onverwacht tot stilstand en verloor het werk van de gebruikers. Ontwikkelaars moesten snel de throw in destructors vervangen door logging en correcte afhandeling.


Verhaal

Een microservice behandelde uitzonderingen op de manier: catch(std::exception). Een van de threads gooide uitzonderingen van gebruikersspecifieke types (die niet waren afgeleid van std::exception). Dergelijke fouten werden niet opgevangen, wat leidde tot een onverwachte verbroken verbinding en geheugenlekken.


Verhaal

Het ontbreken van de noexcept specificator in de verplaatsingsmethoden van containers leidde ertoe dat de standaardbibliotheek een langzame kopie gebruikte in plaats van een snelle move-operatie; een significante prestatieverslechtering werd pas aan het licht gebracht tijdens belastingstests.