1

私が書いているアプリケーションでは、ほとんどのエラー処理で例外を使用しています。私はまだ独自の例外クラスを定義していません。次のことを行っただけです。

namespace Mage {
    typedef std::exception Exception;
}

このようにして、後で同じインターフェイスを使用する必要がある独自の型を定義するときに、すべてのコードを変更する必要がなくなります。

つまり、例外が発生するとアプリケーションがクラッシュします。上記の定義を念頭に置いて、なぜこれがクラッシュするのでしょうか?

void Mage::Root::initialize(Mage::String& p_log) {
    // initialize GLFW and GLEW.
    if (!glfwInit()) {
        throw new Mage::Exception("failed to initialize OpenGL");
        return;
    } else m_GLFWInitialized = true;

「新規」を削除しても保持しても、クラッシュします。何か不足していますか?私はチュートリアルを調べましたが、それらは私を賢くしません。

ここでもエラーをキャッチします:

try {
    MAGE_ROOT.initialize(Mage::String("Mage.log"));
} catch (Mage::Exception& e) {
    std::cerr << e.what() << std::endl;
}

私が得ているクラッシュは次のとおりです。

Debug Error!

Program: ...sual Studio 2010\Project\Mage3D\Binaries\Debug\Test.exe

R6010
- abort() has been called

(Press Retry to debug application)
4

3 に答える 3

11

問題は、例外をキャッチしていないことです。

例外をキャッチする必要があることを知りませんでした(コメントから

はい、そうしなければなりません。スローされた例外をキャッチしない場合は、std::terminate()が呼び出されます。これは意図された動作です。例外は、プログラマがエラー処理を忘れないようにするために存在します。

これは言った、私は提案します:

  • 値渡し;
  • 参照によるキャッチ

例えば:

void foo()
{
    // ...
    throw std::logic_error("Error!");
    //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //    Throw by value (std::logic_error derives from std::exception)
    // ...
}

void bar()
{
    try
    {
        // ...
        foo();
        // ...
    }
    catch (std::exception& e)
           ^^^^^^^^^^^^^^^
    //     Catch by reference
    {
        std::cout << e.what(); // For instance...
    }
}

アップデート:

投稿したコードに関しては、ポインターをスローし、参照によってキャッチしています。ハンドラが一致しません。他に一致するハンドラがないため、std::terminate()が呼び出されます。

代わりに、値によって例外をスローする必要があります。

throw Mage::Exception("failed to initialize OpenGL");

投稿したコードが実際に使用しているコードである場合は、制御がハンドラーに転送されていることがわかります。

于 2013-04-26T12:50:03.087 に答える
2

エラー メッセージに基づいて、プロジェクトに Visual Studio (2010) を使用しています。スローを try/catch ブロックでラップしない限り、スローは「屋根を通り抜け」、C++ ランタイムによって「処理」されます。つまり、abort() が呼び出されます。おそらく、コールスタックの上位に次のようなものが必要です。

try
{
   SomeFunctionThatUltimatelyThrows();
}
catch(Exception & e)
{
   // .. handle error - log, resume, exit, whatever
}

常に参照によって例外をキャッチするという Scott Meyers のアドバイスにも注意してください。「例外」: ただし、MFC CExceptions を使用している場合は、ポインターによってキャッチし、ヒープベースの例外を自己破壊するために Delete メソッドを呼び出します。

編集に基づいて、「ポインターによる」スローと「参照による」キャッチが一致しない場合があります。これを解決してもまだ catch ブロックを実行できない場合は、CRT SetAbortHandler を使用して独自の中止関数をインストールすることにより、abort() 呼び出しのデバッグを試みることができます。これは単に既存のものに連鎖する可能性がありますが、ブレークポイントを設定し、コール スタックを調べて何が問題なのかを確認する機会を提供します。

于 2013-04-26T12:49:46.537 に答える
1

ダミー用の C++ の try-catch-throw ロジック。これは RAII / スタックベースの割り当て / 破棄をカバーしていないことに注意してください。

  • 例外をスローすると、例外は「伝播中」と言われます。それを処理できる最初のハンドラーが見つかるまで (キャッチされる)、またはコール スタックのルートに到達するまで、コール スタックを伝播します。
    • キャッチされた場合、例外がキャッチされた時点から実行が続行されます。例外は、catch ブロックの最後で破棄されます。
    • ルートが見つかった場合、std::unhandled_exception を呼び出します。これは通常 std::terminate を呼び出し、通常は abort() を呼び出します。要するに、すべてができるだけ早く落とされました。
  • 例外が現在伝播しているときに例外をスローすると、一度に 2 つの伝播が発生します。Java と C# にはこれに対処するための巧妙な方法がありますが、そもそもこれが発生することはありません。例外の組み合わせを論理的に処理する例外ハンドラはありません。例外が現在伝播されている間は、例外をスローしないでください。使用すべきではない std::uncaught_exception() を使用しなくても、このルールを保持するのはそれほど難しくありません。
  • スタックの巻き戻し/例外の伝播中に、スタック上で見つかったすべてのオブジェクトが破棄されます。これらのデストラクタは決して例外をスローすべきではありません。結局のところ、オブジェクトの破棄が「失敗」した場合、デストラクタの後でそれを修正するために他に何をするのでしょうか?
  • 常に値でスローし、参照でキャッチします。ポインターでスローしてキャッチすると、おそらく何かをリークする可能性がありますが、これは参照では不可能です。値でキャッチすると、派生した例外の種類が切り捨てられます。参照によってキャッチします。
  • ソフトウェアのルートに、キャッチオール - を含めますcatch(...)。これでは、何をキャッチしたかを正確に知ることはできませんが、少なくとも安全にクラッシュすることはできます。これは、呼び出されたコードが知らない「何か」をスローする可能性がある場合にも行います。
于 2013-04-26T14:02:54.390 に答える