web-dev-qa-db-fra.com

exception c ++: lancer std :: string

J'aimerais lancer une exception lorsque mes méthodes C++ rencontrent quelque chose d'étrange et ne peuvent pas récupérer. Est-il acceptable de lancer un std::string pointeur?

Voici 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;
    }
}
72
Palad1

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

boost a un excellent document sur un bon style pour les exceptions et la gestion des erreurs. Cela vaut la peine d'être lu.

97
christopher_f

Quelques principes:

  1. vous avez une classe de base std :: exception, vos exceptions devraient en dériver. De cette façon, le gestionnaire d'exceptions général a encore quelques informations.

  2. Ne jetez 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 puis utilisez-le 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;
  }
}
59
PierreBdR

Tous ces travaux:

#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 explicitement la mémoire.

21
anon

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 devez créer des temporaires anonymes pour que le compilateur traite de la durée de vie de l'objet que vous lancez. Si vous jetez quelque chose de nouveau dans le tas, quelqu'un d'autre doit le libérer.
  • Vous devriez attraper des références pour empêcher le découpage d'objet

.

Voir "Effective C++ - 3rd edition" de Meyer pour plus de détails ou visitez le site https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+ référence

7
Michael Burr

Cela fonctionne, mais je ne le ferais pas si j'étais toi. Il semble que vous ne supprimiez pas ces données lorsque vous avez terminé, ce qui signifie que vous avez créé une fuite de mémoire. Le compilateur C++ veille à ce que les données sur les exceptions soient maintenues en vie même lorsque la pile est éjectée. Ne vous sentez donc pas obligé d'utiliser le tas.

Incidemment, jetant 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 wrapper. Cela peut juste encapsuler un string pour le moment, mais peut-être qu’à l'avenir vous voudrez inclure d'autres informations, telles que des données qui ont provoqué 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 grande route maintenant et ne jetez pas d'objets bruts.

7
Daniel Spiewak

Le moyen le plus simple de lancer une exception en C++:

#include <iostream>
using namespace std;
void purturb(){
    throw "Cannot purturb at this time.";
}
int main() {
    try{
        purturb();
    }
    catch(const char* msg){
        cout << "We caught a message: " << msg << endl;
    }
    cout << "done";
    return 0;
}

Cela imprime:

We caught a message: Cannot purturb at this time.
done

Si vous attrapez l'exception levée, l'exception est contenue et le programme continuera. Si vous n'acceptez pas l'exception, le programme existe et affiche:

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

4
Eric Leschinski

Bien que cette question soit plutôt ancienne et que l'on y ait déjà répondu, je souhaite simplement ajouter une note expliquant comment gérer correctement les exceptions en C++ 11 :

Utilisation std::nested_exception et std::throw_with_nested

À mon avis, leur utilisation conduit à une conception plus nette des exceptions et rend inutile la création d'une hiérarchie de classes d'exceptions.

Notez que cela vous permet d’avoir une trace de vos exceptions dans votre code sans avoir besoin d’un débogueur ou d’une journalisation fastidieuse. Il est décrit sur StackOverflow ici et ici , comment écrire un gestionnaire d'exceptions approprié qui va redistribuer les exceptions imbriquées.

Comme vous pouvez le faire avec n'importe quelle classe d'exception dérivée, vous pouvez ajouter beaucoup d'informations à un tel backtrace! Vous pouvez également jeter un oeil à mon MWE sur GitHub , où une trace aurait l'apparence suivante:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
1
GPMueller