web-dev-qa-db-ja.com

C ++例外をスローする方法

例外処理(つまり、自分の目的のためにthrow、try、catchステートメントをカスタマイズする方法)については非常によく理解していません。

例えば、私は以下のように関数を定義しました:int compare(int a, int b){...}

Aまたはbのいずれかが負の場合、この関数は何らかのメッセージを出して例外をスローします。

関数の定義でこれにどのように取り組むべきですか?

199
Terry Li

シンプル:

#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( ... ) { };
288
nsanders

必要に応じて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 を調べてください。

16
Cat Plus Plus

この質問はかなり古く、すでに回答されていますが、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"
12
GPMueller

特定のエラーが発生したときにスローするメッセージを定義できます。

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; }
6
serup

カスタム例外の場合、ここで説明されている他の回答に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を加えることだけであると私は言います(この点は言及する価値があると思いました、まだコメント内に描写することはできませんでした)。

3
Guy Avraham