75 votes

c++ exception : throwing std::string

J'aimerais lancer une exception lorsque mes méthodes C++ rencontrent quelque chose de bizarre et ne peuvent pas s'en remettre. Est-il possible de lancer un pointeur std::string ?

Voilà ce que j'avais hâte de faire :

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw new std::String("it's the end of the world!");
  }
}

void Foo:Caller(){
  try{
    this->Bar();// should throw
  }catch(std::String* caught){ // not quite sure the syntax is ok here...
    std::cout<<"Got "<<caught<<std::endl;
  }
}

98voto

christopher_f Points 1239

Oui. std::exception est la classe d'exception de base de la bibliothèque standard C++. Vous voudrez peut-être éviter d'utiliser des chaînes de caractères comme classes d'exception, car elles peuvent elles-mêmes lever une exception pendant leur utilisation. Si cela se produit, où serez-vous ?

boost a un excellent document sur le bon style pour les exceptions et la gestion des erreurs. Cela vaut la peine de le lire.

61voto

PierreBdR Points 11479

Quelques principes :

  1. si vous avez une classe de base std::exception, vous devriez faire dériver vos exceptions de celle-ci. De cette façon, le gestionnaire d'exception général dispose toujours de certaines informations.

  2. Ne lancez pas de pointeurs mais des objets, de cette façon la mémoire est gérée pour vous.

Exemple :

struct MyException : public std::exception
{
   std::string s;
   MyException(std::string ss) : s(ss) {}
   ~MyException() throw () {} // Updated
   const char* what() const throw() { return s.c_str(); }
};

Et ensuite l'utiliser dans votre code :

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw MyException("it's the end of the world!");
  }
}

void Foo::Caller(){
  try{
    this->Bar();// should throw
  }catch(MyException& caught){
    std::cout<<"Got "<<caught.what()<<std::endl;
  }
}

22voto

Tout cela fonctionne :

#include <iostream>
using namespace std;

//Good, because manual memory management isn't needed and this uses
//less heap memory (or no heap memory) so this is safer if
//used in a low memory situation
void f() { throw string("foo"); }

//Valid, but avoid manual memory management if there's no reason to use it
void g() { throw new string("foo"); }

//Best.  Just a pointer to a string literal, so no allocation is needed,
//saving on cleanup, and removing a chance for an allocation to fail.
void h() { throw "foo"; }

int main() {
  try { f(); } catch (string s) { cout << s << endl; }
  try { g(); } catch (string* s) { cout << *s << endl; delete s; }
  try { h(); } catch (const char* s) { cout << s << endl; }
  return 0;
}

Vous devriez préférer h à f à g. Notez que dans l'option la moins préférable, vous devez libérer la mémoire explicitement.

8voto

Daniel Spiewak Points 30706

Ça marche, mais je ne le ferais pas si j'étais vous. Vous ne semblez pas effacer les données de la pile lorsque vous avez terminé, ce qui signifie que vous avez créé une fuite de mémoire. Le compilateur C++ prend soin de s'assurer que les données d'exception sont maintenues en vie même lorsque la pile est vidée, donc ne vous sentez pas obligé d'utiliser le tas.

Par ailleurs, le fait de lancer un std::string n'est pas la meilleure approche pour commencer. Vous aurez beaucoup plus de flexibilité sur la route si vous utilisez un simple objet d'encapsulation. Il peut simplement encapsuler un string pour l'instant, mais peut-être qu'à l'avenir vous voudrez inclure d'autres informations, comme des données qui ont causé l'exception ou peut-être un numéro de ligne (très commun, ça). Vous ne voulez pas changer toute votre gestion des exceptions à chaque endroit de votre base de code, alors prenez la bonne voie maintenant et ne lancez pas d'objets bruts.

8voto

Michael Burr Points 181287

En plus de lancer probablement quelque chose dérivé de std::exception, vous devriez lancer des temporaires anonymes et attraper par référence :

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw std::string("it's the end of the world!");
  }
}

void Foo:Caller(){
  try{
    this->Bar();// should throw
  }catch(std::string& caught){ // not quite sure the syntax is ok here...
    std::cout<<"Got "<<caught<<std::endl;
  }
}
  • Vous devriez lancer les temporaires anonymes anonymes pour que le compilateur traite de la durée de vie de l'objet que vous vous lancez - si vous lancez quelque chose de nouveau sur le tas, quelqu'un d'autre doit le libérer chose.
  • Vous devez attraper les références pour empêcher le découpage en tranches des objets

.

Pour plus de détails, consultez l'ouvrage de Meyer intitulé "Effective C++ - 3rd edition" ou rendez-vous sur le site https://www.securecoding.cert.org/.../ERR02-A.+Lancer+anonyme+temporaire+et+catch+par+référence

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X