ProgrammationDéveloppeur système C++

Parlez des mécanismes de travail des exceptions en C++. Comment traiter les erreurs correctement et à quoi sert noexcept ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question :

Le mécanisme de gestion des exceptions a été introduit en C++ pour assurer un traitement des erreurs plus fiable et structuré, contrairement aux codes de retour. Au fil des ans, la syntaxe s'est élargie, et des spécificateurs comme noexcept ont été ajoutés pour contrôler l'annulation du lancement d'exceptions à partir de fonctions.

Problème :

Une mauvaise gestion des exceptions entraîne souvent des fuites de mémoire, un comportement indéfini ou des plantages d'applications. Si l'on ne prend pas en compte le lancement d'exceptions dans les constructeurs, les destructeurs ou lors de la manipulation de ressources, cela engendre un problème sérieux avec l'état du programme.

Solution :

La syntaxe de base — l'utilisation de try-catch. Les exceptions peuvent être de n'importe quel type, mais il est recommandé d'hériter des exceptions utilisateur de std::exception. Le mot-clé noexcept (C++11+) indique que la fonction ne doit pas lancer d'exceptions ; le lancer d'une exception depuis une fonction noexcept appelle std::terminate().

Exemple de code :

#include <iostream> #include <stdexcept> void func() noexcept(false) { throw std::runtime_error("Erreur !"); } int main() { try { func(); } catch(const std::exception& ex) { std::cout << ex.what(); } } }

Caractéristiques clés :

  • Les exceptions assurent un unwind automatique (déroulement de pile)
  • noexcept permet au compilateur d'optimiser si la fonction ne lance pas
  • Seules les std::exception et ses dérivées garantissent la présence de la méthode what()

Questions pièges.

Est-il acceptable de lancer des exceptions dans un destructeur ?

Non, le lancement d'une exception dans un destructeur pendant le déroulement de la pile entraîne std::terminate(). Les exceptions dans le destructeur doivent être gérées à l'intérieur du destructeur lui-même.

Que se passe-t-il si une exception est lancée depuis une fonction noexcept ?

std::terminate sera appelé, le programme se terminera de manière abrupte. noexcept est un contrat strict.

Peut-on attraper des exceptions par valeur ? Quelles en sont les dangers ?

On peut attraper par valeur, mais l'objet est copié (object slicing). Il est recommandé d'utiliser const &.

Exemple de code :

try { throw std::out_of_range("err"); } catch (const std::exception& e) { /* ... */ }

Erreurs typiques et anti-patterns

  • Utilisation de throw dans les destructeurs
  • Absorption des exceptions catch(...) sans journalisation
  • Non-déclaration de noexcept là où cela est possible

Exemple de la vie réelles

Cas négatif

Le code lançait des exceptions dans les destructeurs de ressources ; lors d'une terminaison abrupte, la pile était mal déroulée, et les ressources sont restées en fuite.

Avantages :

  • Détection rapide des erreurs possible

Inconvénients :

  • Les programmes se terminaient souvent avec une erreur terminate

Cas positif

Les méthodes critiques étaient marquées noexcept, les destructeurs traitaient toutes les exceptions à l'intérieur. Des catch par référence étaient utilisés, toutes les erreurs étaient journalisées.

Avantages :

  • Fiabilité, absence de fuites
  • Comportement extrêmement prévisible

Inconvénients :

  • Rigueur accrue demandée au développeur lors de l'écriture de code correct et "propre"