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


260

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

たとえば、次のように関数を定義しました。 int compare(int a, int b){...}

aまたはbのいずれかが負の場合に、関数がメッセージを表示して例外をスローするようにしたい

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



37
@OliCharlesworth、それは基本に混乱している誰かに向けるのに少し多すぎると思いませんか?
Mark Ransom 2011

6
余分な例外は避ける価値があります。呼び出し元に負の値を渡させたくない場合unsigned intは、関数シグネチャのパラメータとして指定することで、より明確になります。それから、私はあなたが実際に例外的なもののために例外を投げて捕まえるだけでよい学校です。
AJG85

1
@マーク:私はもともとthrow()、関数に例外仕様を使用すべきかどうかについての質問を誤解していました。
Oliver Charlesworth

回答:


364

シンプル:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

標準ライブラリには、スローできる組み込みの例外オブジェクトの優れたコレクションが付属しています。常に値でスローし、参照でキャッチする必要があることに注意してください。

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

26
また、例外は常にconstとしてキャッチする必要があります
Adrian Cornish

2
@TerryLiYifengカスタム例外がより理にかなっている場合はそれのために行きます。std :: exceptionから派生させて、インターフェースを同じにしたい場合もあります。
nsanders

2
もう一度+1しましたが、constはかなり重要だと思います。これは、一時オブジェクトであるという事実を浮き彫りにしているためです。したがって、変更は役に立ちません。
エイドリアンコーニッシュ

2
@AdrianCornish:それは本当に一時的なものではありません。非constキャッチが役立つ場合があります。
GManNickG 2011

26
通常は、throw;(元のオブジェクトを再スローしてそのタイプを保持する)単純な方法で再スローしますthrow e;(キャッチしたオブジェクトのコピーをスローし、タイプを変更する可能性があります)。
Mike Seymour

17

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を調べます。


15

この質問はかなり古く、すでに回答されていますが、C ++ 11で適切な例外処理を行う方法についてのメモを追加したいだけです。

使用std::nested_exceptionしてstd::throw_with_nested

StackOverflowのここここで、ネストされた例外を再スローする適切な例外ハンドラーを作成するだけで、デバッガーや面倒なロギングを必要とせずに、コード内の例外のバックトレースを取得する方法について説明しています。

派生した例外クラスでこれを行うことができるため、そのようなバックトレースに多くの情報を追加できます。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"

8

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

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

ここで説明されている他の回答に、カスタム例外の場合の追加のメモを追加したかった。

から派生する独自のカスタム例外を作成する場合、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句は、あなたが何をしたか、おそらくである(実行されますしないで)最初の場所にしたかったです。

2)最初のcatch句でキャッチされた型がtypeであってもstd::exception、「適切な」バージョンのwhat()が呼び出されます-参照によってキャッチされるため(少なくともキャッチされた引数のstd::exception型を値によって変更することで)、動作中の「オブジェクトのスライス」現象)。

3)「XXX例外がスローされたために一部のコードが例外タイプに対して重要な処理を行う」場合、ここにコードの不正な動作があります。

4)これは、キャッチしたオブジェクトが次のような「通常の」オブジェクトである場合にも関連class Base{};class Derived : public Base {}ます。

5)g++ 7.3.0Ubuntu 18.04.1では、上記の問題を示す警告が表示されます。

関数 'void IllustrateDerivedExceptionCatch()'で:item12Linux.cpp:48:2:警告:タイプ 'MyException'の例外がキャッチされますcatch(const MyException&e)^ ~~~~

item12Linux.cpp:43:2:警告: 'std :: exception' キャッチの初期ハンドラー(const例外&e)^ ~~~~

繰り返しますが、この回答は、ここで説明されている他の回答に追加することのみを目的としています(この点は言及する価値があると思いましたが、コメント内に描写することはできませんでした)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.