スタックに例外がどのように範囲を獲得したのですか?
-
18-09-2019 - |
質問
次のコードでは、スタックベースの変数「EX」がスローされ、EXが宣言された範囲を超えた関数に巻き込まれます。 (AFAIK)スタックベースの変数は、それらが宣言されたスコープの外側で使用できないため、これは私には少し奇妙に思えます(スタックは巻き付けられていません)。
void f() {
SomeKindOfException ex(...);
throw ex;
}
void g() {
try {
f();
} catch (SomeKindOfException& ex) {
//Handling code...
}
}
SomekindofexceptionのDestructorに印刷声明を追加しましたが、F()で範囲外に出るとEXが破壊されることを示していますが、G()に巻き込まれ、範囲外になります。
何か助けがありますか?
解決
例外オブジェクトは、スタックの巻き戻しに耐えるために特別な場所にコピーされます。 2つの破壊が見られる理由は、F()を終了すると元の例外が破壊され、g()を終了するとコピーが破壊されるためです。
他のヒント
オブジェクトはanにコピーされます 例外オブジェクト それはスタックの不定段階に耐えます。そのオブジェクトのメモリが由来する場所は、特定されていません。大きなオブジェクトの場合、おそらくそうなるでしょう malloc
'ed、およびより小さなオブジェクトの場合、実装には事前に割り当てられたバッファーがある可能性があります(これが使用されると想像できます。 bad_alloc
例外)。
参照 ex
その後、それに縛られます 例外オブジェクト, 、一時的なものです(名前はありません)。
C ++標準15.1/4:
スローされる例外の一時コピーのメモリは、3.7.3.1に記載されている場合を除き、不特定の方法で割り当てられます。その例外のためにハンドラーが実行されている限り、一時的なものは持続します。特に、スローを実行してハンドラーが終了する場合。声明、同じ例外のために別のハンドラーに制御を渡すので、一時的なものが残ります。例外のために実行されている最後のハンドラーがスロー以外の手段によって終了するとき。一時的なオブジェクトが破壊され、実装は一時的なオブジェクトのメモリを扱うことができます。このような取引は、不特定の方法で行われます。破壊は、ハンドラー内の例外決定で宣言されたオブジェクトの破壊の直後に発生します。
これ以上言うことはありません。
Exをスローすると、スローされた例外オブジェクトに使用される特別なメモリ位置にコピーされます。このようなコピーは、通常のコピーコンストラクターによって実行されます。
この例からこれを簡単に見ることができます。
#include <iostream>
void ThrowIt();
class TestException
{
public:
TestException()
{
std::cerr<<this<<" - inside default constructor"<<std::endl;
}
TestException(const TestException & Right)
{
(void)Right;
std::cerr<<this<<" - inside copy constructor"<<std::endl;
}
~TestException()
{
std::cerr<<this<<" - inside destructor"<<std::endl;
}
};
int main()
{
try
{
ThrowIt();
}
catch(TestException & ex)
{
std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
}
return 0;
}
void ThrowIt()
{
TestException ex;
throw ex;
}
サンプル出力:
matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor
ちなみに、スローされたオブジェクト(0x09EC0068)に使用されるメモリの位置は、元のオブジェクトの1つ(0xBF8E202F)から間違いなく遠く離れていることがわかります。通常のように、スタックは高いアドレスを持っていますが、スローされたオブジェクトは、仮想アドレス空間にかなり下がっています。それでも、これは実装の詳細です。なぜなら、他の回答が指摘したように、標準はスローされたオブジェクトのメモリがどこにあるべきか、どのように割り当てられるべきかについて何も言っていないからです。
標準が15.1/4(「例外を処理/スローする例外」)で述べていることに加えて - スローされている例外の一時コピーのメモリは、不特定の方法で割り当てられていること - 例外オブジェクトに割り当てられます:
標準の3.7.3.1/4( "割り当て関数")は、例外オブジェクトをによって割り当てることができないことを示しています
new
「グローバル割り当て関数」への式または呼び出し(つまり、operator new()
置換)。ご了承くださいmalloc()
標準で定義されている「グローバル割り当て関数」ではないので、malloc()
例外オブジェクトを割り当てるためのオプションです。「例外がスローされると、例外オブジェクトが作成され、一般的に何らかの例外データスタックに配置されます」(Stanley Lippman、「C ++オブジェクトモデル内」-7.2例外処理)
Stroustrupの「C ++プログラミング言語、第3版」から:「C ++の実装が必要になるには、投げるのに十分な予備のメモリが必要です
bad_alloc
記憶の疲労の場合。ただし、他の例外を投げるとメモリの疲労を引き起こす可能性があります。」(14.4.5リソースの使い果たし)。「実装は、例外を保存および送信するためのさまざまな戦略を適用する可能性があります。ただし、許可するのに十分なメモリがあることが保証されていますnew
標準のメモリの例外をスローするには、bad_alloc
「(14.3キャッチングの例外)。
Stroustrupからの引用は標準以前であることに注意してください。 Stroustrupが2回言及するほど重要だと考えたという保証を標準ではないように見えるのは面白いと思います。
仕様は明示的に述べているため、一時的なオブジェクトがその代わりに作成されることを throw
オペランド。