私のC++メソッドが奇妙な何かに遭遇し、回復できない場合に例外をスローしたいと思います。 std::string
ポインターを投げても大丈夫ですか?
これが私が楽しみにしていたことです。
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;
}
}
はい。 std::exception
は、C++標準ライブラリの基本例外クラスです。使用中に文字列自体が例外をスローする可能性があるため、文字列を例外クラスとして使用しないようにすることができます。それが起こった場合、あなたはどこにいますか?
boostには優れた document があり、例外とエラー処理に適したスタイルです。読む価値があります。
いくつかの原則:
std :: exception基本クラスがある場合、例外を派生させる必要があります。このように、一般的な例外ハンドラーにはまだ情報があります。
ポインタを投げるのではなく、オブジェクトを投げてください。そうすれば、メモリが自動的に処理されます。
例:
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(); }
};
そして、コードでそれを使用します。
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;
}
}
これらはすべて機能します。
#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;
}
Hをfよりもgに優先する必要があります。最も好ましくないオプションでは、メモリを明示的に解放する必要があることに注意してください。
おそらくstd :: exceptionから派生したものをスローすることに加えて、匿名のテンポラリーをスローし、参照でキャッチする必要があります。
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;
}
}
。
詳細については、Meyerの「Effective C++-3rd edition」を参照するか、 https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+にアクセスしてください。参照
それは機能しますが、私があなただったら私はそれをしません。完了時にそのヒープデータを削除していないようです。つまり、メモリリークが発生しています。 C++コンパイラーは、スタックがポップされても例外データが保持されるようにするため、ヒープを使用する必要があるとは感じないでください。
ちなみに、std::string
は、最初から最善のアプローチではありません。単純なラッパーオブジェクトを使用すると、今後さらに柔軟性が高まります。現時点ではstring
をカプセル化しているだけかもしれませんが、将来的には、例外の原因となったデータや行番号(非常に一般的なもの)など、他の情報を含めたいかもしれません。コードベースのすべての場所で例外処理のすべてを変更したくないので、今すぐに進んで、生のオブジェクトを投げないでください。
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;
}
これは印刷します:
We caught a message: Cannot purturb at this time.
done
スローされた例外をキャッチすると、例外が含まれ、プログラムが続行されます。例外をキャッチしなかった場合、プログラムが存在し、出力されます:
This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
この質問はかなり古く、すでに回答されていますが、適切な例外処理を行う方法に関するメモを追加したいだけですin C++ 11:
std::nested_exception
および std::throw_with_nested
私の意見では、これらを使用すると、例外の設計がよりクリーンになり、例外クラス階層を作成する必要がなくなります。
これにより、デバッガーや面倒なロギングを必要とせずに、コード内で例外のバックトレースを取得できることに注意してください。 StackOverflow here および here で、ネストされた例外を再スローする適切な例外ハンドラーを記述する方法について説明します。
派生した例外クラスでこれを行うことができるため、このようなバックトレースに多くの情報を追加できます!また、私の GitHubのMWE を見ると、バックトレースは次のようになります。
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"