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;
}
}
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.
Quelques principes:
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.
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;
}
}
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.
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;
}
}
.
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
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.
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.
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 :
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"