web-dev-qa-db-ja.com

C ++例外処理ランタイムはどのように実装されていますか?

C++の例外処理メカニズムのしくみに興味をそそられます。具体的には、例外オブジェクトはどこに格納され、それがキャッチされるまでいくつかのスコープを介してどのように伝播しますか?一部のグローバルエリアに保存されていますか?

これはコンパイラ固有である可能性があるため、誰かがg ++コンパイラスイートのコンテキストでこれを説明できますか?

76
Paul Joseph

実装は異なる場合がありますが、要件から従​​ういくつかの基本的なアイデアがあります。

例外オブジェクト自体は、1つの関数で作成され、その呼び出し元で破棄されるオブジェクトです。したがって、通常、スタック上にオブジェクトを作成することはできません。一方、多くの例外オブジェクトはそれほど大きくありません。エルゴ、例えば32バイトのバッファを作成し、より大きな例外オブジェクトが実際に必要な場合はオーバーフローしてヒープに入れることができます。

実際の支配権の移転については、2つの戦略が存在します。 1つは、スタックを巻き戻すのに十分な情報をスタック自体に記録することです。これは基本的に、実行するデストラクタと、例外をキャッチする可能性のある例外ハンドラのリストです。例外が発生したら、一致するキャッチが見つかるまで、これらのデストラクタを実行してスタックを実行します。

2番目の戦略では、この情報をスタックの外部のテーブルに移動します。現在は、例外が発生すると、コールスタックを使用して、どのスコープに入っているのにどのスコープが出ていないかを調べます。次に、それらは静的テーブルで検索され、スローされた例外が処理される場所と、その間に実行されるデストラクタが決定されます。これは、スタックでの例外オーバーヘッドが少ないことを意味します。とにかく返信アドレスが必要です。テーブルは追加のデータですが、コンパイラはそれらをプログラムのデマンドロードセグメントに配置できます。

43
MSalters

これは、「15.1標準の例外のスロー」で定義されています。

スローによって一時オブジェクトが作成されます。
この一時オブジェクトのメモリの割り当て方法は指定されていません。

一時オブジェクトの作成後、コントロールは呼び出しスタック内の最も近いハンドラーに渡されます。スローとキャッチポイントの間でスタックを巻き戻します。スタックが巻き戻されると、スタック変数は作成とは逆の順序で破棄されます。

例外が再スローされない限り、一時オブジェクトは、それがキャッチされたハンドラーの最後で破棄されます。

注:参照でキャッチする場合、参照は一時を参照します。値でキャッチする場合、一時オブジェクトは値にコピーされます(したがって、コピーコンストラクターが必要です)。

S.Meyersからのアドバイス(const参照でキャッチ)。

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}
18
Martin York

詳細な説明については、 ここ を参照してください。

また、基本的な種類の例外処理を実装するためにプレーンCで使用されているトリックを確認することも役立ちます。これは、setjmp()とlongjmp()を次のように使用することを伴います。前者は例外ハンドラ(「catch」など)をマークするためにスタックを保存し、後者は値を「スロー」するために使用されます。 「スローされた」値は、呼び出された関数から返されたように見えます。 「tryブロック」は、setjmp()が再度呼び出されるか、関数が戻ると終了します。

8
Jules May