例外処理(つまり、自分の目的のためにthrow、try、catchステートメントをカスタマイズする方法)については非常によく理解していません。
例えば、私は以下のように関数を定義しました:int compare(int a, int b){...}
Aまたはbのいずれかが負の場合、この関数は何らかのメッセージを出して例外をスローします。
関数の定義でこれにどのように取り組むべきですか?
シンプル:
#include <stdexcept>
int compare( int a, int b ) {
if ( a < 0 || b < 0 ) {
throw std::invalid_argument( "received negative value" );
}
}
標準ライブラリには、スローできる 組み込み例外オブジェクト のNiceコレクションが付属しています。あなたは常に値で投げ、参照によって捕らえるべきであることを覚えておいてください:
try {
compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
// do stuff with exception...
}
各試行の後に複数のcatch()ステートメントを使用できるため、必要に応じてさまざまな例外タイプを別々に処理できます。
例外を再スローすることもできます。
catch( const std::invalid_argument& e ) {
// do something
// let someone higher up the call stack handle it if they want
throw;
}
型に関係なく例外を捕捉するには
catch( ... ) { };
必要に応じてthrow
を追加し、エラーを処理する呼び出し元にtry
ブロックを追加するだけです。慣例により、std::exception
から派生するものだけをスローするようにします。したがって、最初に<stdexcept>
を含めます。
int compare(int a, int b) {
if (a < 0 || b < 0) {
throw std::invalid_argument("a or b negative");
}
}
void foo() {
try {
compare(-1, 0);
} catch (const std::invalid_argument& e) {
// ...
}
}
また、 Boost.Exception を調べてください。
この質問はかなり古く、すでに回答されていますが、C++ 11で適切な例外処理を行う方法についてのメモを追加します。
std::nested_exception
および std::throw_with_nested
を使用StackOverflow はここ と はここ で説明されていますが、どのようにデバッガや面倒なロギングを必要とせずに、ネストされた例外を再スローする適切な例外ハンドラを作成することで、コード内のexceptionsのバックトレースを取得します。
これは任意の派生例外クラスで実行できるので、そのようなバックトレースに多くの情報を追加できます。また、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"
特定のエラーが発生したときにスローするメッセージを定義できます。
throw std::invalid_argument( "received negative value" );
あるいは、このように定義することもできます。
std::runtime_error greatScott("Great Scott!");
double getEnergySync(int year) {
if (year == 1955 || year == 1885) throw greatScott;
return 1.21e9;
}
通常、try ... catch
ブロックはこのようになります。
try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }
カスタム例外の場合、ここで説明されている他の回答にADDを付け加えたいと思いました。
std::exception
から派生する独自のカスタム例外を作成する場合、「すべての可能性のある」例外タイプをキャッチするときは、必ずcatch
句を「最も派生する」例外タイプで開始する必要があります。例を参照してください(何をするしない):
#include <iostream>
#include <string>
using namespace std;
class MyException : public exception
{
public:
MyException(const string& msg) : m_msg(msg)
{
cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
}
~MyException()
{
cout << "MyException::~MyException" << endl;
}
virtual const char* what() const throw ()
{
cout << "MyException - what" << endl;
return m_msg.c_str();
}
const string m_msg;
};
void throwDerivedException()
{
cout << "throwDerivedException - thrown a derived exception" << endl;
string execptionMessage("MyException thrown");
throw (MyException(execptionMessage));
}
void illustrateDerivedExceptionCatch()
{
cout << "illustrateDerivedExceptionsCatch - start" << endl;
try
{
throwDerivedException();
}
catch (const exception& e)
{
cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
// some additional code due to the fact that std::exception was thrown...
}
catch(const MyException& e)
{
cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
// some additional code due to the fact that MyException was thrown...
}
cout << "illustrateDerivedExceptionsCatch - end" << endl;
}
int main(int argc, char** argv)
{
cout << "main - start" << endl;
illustrateDerivedExceptionCatch();
cout << "main - end" << endl;
return 0;
}
注:
0)正しい順序はその逆であるべきです。すなわち、最初にあなたがcatch (const MyException& e)
に続いてcatch (const std::exception& e)
です。
1)ご覧のとおり、そのままプログラムを実行すると、最初のcatch節が実行されます(これはおそらく最初に望んでいたNOTです)。
2)最初のcatch節で捉えられた型がstd::exception
型であっても、 "適切な"バージョンのwhat()
が呼ばれるでしょう - それは参照によって捉えられるので(少なくとも捉えられた引数std::exception
型は値によるものに変更 - そしてアクションで「オブジェクトスライシング」現象を経験します。
3) "XXX例外がスローされたという事実に起因するコード..."が例外タイプに関して重要なことをしている場合、ここにあなたのコードの誤動作があります。
4)class Base{};
やclass Derived : public Base {}
...のように、捕捉されたオブジェクトが「通常の」オブジェクトである場合もこれは関係があります。
5)Ubuntu 18.04.1のg++ 7.3.0
は上記の問題を示す警告を出します。
関数 'void explainDerivedExceptionCatch()':item12Linux.cpp:48:2:warning:型 'MyException' の例外がキャッチされます(const MyException&e)^ ~~~~
item12Linux.cpp:43:2:警告: 'std :: exception' catchの前のハンドラで(const exception&e)^ ~~ ~~
繰り返しますが、この答えはここで説明されている他の答えにADDを加えることだけであると私は言います(この点は言及する価値があると思いました、まだコメント内に描写することはできませんでした)。