6

次のプログラムの動作が定義されている場合、それは何ですか?

#include <iostream>
#include <exception>
#include <cstdlib>

void i_throw()
{
    std::cout << "i_throw()" << std::endl;
    // std::terminate() is noexcept so if the terminate handler throws...
    // then the terminate handler is called...
    // std::terminate is [[noreturn]] so don't return
    try
    {
        throw 7;
    }
    catch(...)
    {
        std::cout << "caught exception, re-throw()-ing" << std::endl;
        throw;
    }
    std::cout << "got here!" << std::endl;
    std::abort();
}

int main()
{
    std::set_terminate(i_throw);
    throw;
    std::terminate();
}

gcc と clang を使用すると、次の出力が得られます。

i_throw()
caught exception, re-throw()-ing
Aborted (core dumped)

最初のいくつかのコメントの後に編集された例。

throw;(なぜとの両方があるのか​​わかりませんstd::terminate();。例を変更したくないので、これら 2 つのうちの 1 つだけが存在するふりをします。)

4

1 に答える 1

2

上記の質問は、次の 2 つのコード スニペットの動作を理解することに要約できます。

サンプル 1: アクティブな例外なしでスローする

int main()
{
    try{
        throw;
    }catch(...){
        std::cout<<"caught"<<endl;  //we never reach here
    }
    return 0;
}

上記のコードを実行すると、以下のようにクラッシュします

terminate called without an active exception
Aborted (core dumped)

サンプル 2:アクティブな例外をスローする

int main()
{
    try{
        throw 7;
    }catch(...){
        std::cout<<"caught"<<endl;  //will be caught
    }
    return 0;
}

それを実行すると、予測可能な出力が得られます

caught

コードのアセンブリを生成する場合 ( g++ -S option)。throw と throw 7 の次の cxx_abi 呼び出しに気付くでしょう。

throw;に変換されますcall __cxa_rethrow

throw 7;に変換されますcall __cxa_throw

ここにコードがあります__cxa_throw

extern "C" void
__cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo,
             void (_GLIBCXX_CDTOR_CALLABI *dest) (void *))
{
  PROBE2 (throw, obj, tinfo);

  __cxa_eh_globals *globals = __cxa_get_globals ();
  globals->uncaughtExceptions += 1;

  // code removed for brevity 
  //.......
  // Below code throws an exception to be caught by caller

  #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
    _Unwind_SjLj_RaiseException (&header->exc.unwindHeader);
  #else
    _Unwind_RaiseException (&header->exc.unwindHeader);
  #endif

  // Some sort of unwinding error.  Note that terminate is a handler.
  __cxa_begin_catch (&header->exc.unwindHeader);
  std::terminate ();
}

そのため、OP コードでthrow 7;は、対応することによってキャッチされ、によってcatch(...)再スローされます。throw;

ここにコードがあります__cxa__rethrow

extern "C" void
__cxxabiv1::__cxa_rethrow ()
{
  __cxa_eh_globals *globals = __cxa_get_globals ();
  __cxa_exception *header = globals->caughtExceptions; // We are not re

  globals->uncaughtExceptions += 1;

  // Watch for luser rethrowing with no active exception.
  if (header)
    {
      // Code removed for brevity
      // .....
      // Below code rethrows the exception
      #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
      _Unwind_SjLj_Resume_or_Rethrow (&header->unwindHeader);
      #else
      #if defined(_LIBUNWIND_STD_ABI)
      _Unwind_RaiseException (&header->unwindHeader);
      #else
      _Unwind_Resume_or_Rethrow (&header->unwindHeader);
      #endif
      #endif
    }
  std::terminate ();
}

std::terminate()どちらの場合も、が からまだ呼び出されていないことがわかり__cxx_*ます。上記の abi によってスローされた後、コード内の次の場所にいます。

コードを終了するには、 cxx_abiを参照してください。

void
__cxxabiv1::__terminate (std::terminate_handler handler) throw ()
{
  __try 
    {
      handler ();      // Our handler has thrown an int exception
      std::abort ();
    } 
  __catch(...)  // Exception is caught here and process is aborted.
    { std::abort (); } 
}

void
std::terminate () throw()
{
  __terminate (get_terminate ());
}

概要

私の理解によると、ハンドラーから例外を再スローすると、再スローされた例外が でキャッチされ__cxxabiv1::__terminateます。それが呼び出す場所abort()。明らかに、std::terminate()[from __cxa_rethrow] メソッドは登場しませんでした。それが、コントロールが到達しなかった理由です。 std::cout << "got here!" << std::endl;

無限再帰

terminate_handler を次のように変更するとどうなりますか。

void i_throw()
{
    std::cout << "i_throw()" << std::endl;
    throw;
    std::cout << "got here!" << std::endl;
    std::abort();
}

これを理解するために、__cxa_rethrow()上記のように見ることができます。

スローされているアクティブな例外がないため、__cxa_rethrow()呼び出しが終了しstd::terminate()、無限再帰が発生します。

于 2015-02-27T10:29:36.053 に答える