デストラクタから例外をスローしてはならないことはわかっています。
デストラクタが例外をスローできる関数を呼び出した場合、デストラクタでそれをキャッチし、それ以上スローしなくても問題ありませんか? それとも、とにかくアボートを引き起こす可能性があり、デストラクタからそのような関数を呼び出すべきではありませんか?
はい、それは合法です。例外はデストラクタからエスケープしてはなりませんが、デストラクタ内またはデストラクタが呼び出す関数で何が起こるかはあなた次第です。
(技術的には、例外はデストラクタ呼び出しからもエスケープできます。別の例外がスローされたためにスタックの巻き戻し中にそれが発生した場合は、std::terminate
が呼び出されます。したがって、標準で明確に定義されていますが、これは本当に悪い考えです。)
はい。
例として、標準ライブラリの std::fstream クラスを見てください。
概念は、デストラクタがスローできるメソッドを呼び出す場合、これらのメソッドはパブリックにする必要があるということです。したがって、オブジェクトのユーザーが例外をチェックしたい場合は、パブリック メソッドを使用して例外を処理できます。例外を気にしない場合は、デストラクタに問題を処理させてください。
std::fstream の例に戻ります。
{
std::fstream text("Plop");
// Load Text.
// I don't care if the close fails.
// So let the destructor handle it and discard exceptions
}
{
// If this fails to write I should at least warn the user.
// So in this case I will explicitly try and close it.
try
{
std::ofstram password("/etc/password");
// Update the password file.
password.close();
}
catch(...)
{
Message.ShowDialog("You failed to update the Password File");
}
}
ここでいくつかの例を見つけることができます: https://software.intel.com/sites/products/documentation/doclib/iss/2013/sa-ptr/sa-ptr_win_lin/GUID-D2983B74-74E9-4868-90E0-D65A80F8F69F.htm
伝播中の別の例外のスタックの巻き戻し中に例外がデストラクタを離れた場合、 std::terminate() が呼び出されます。
スタックの巻き戻しが進行中でない場合、 std::terminate() が呼び出されずに例外がデストラクタから出る可能性があります。ただし、ヒープに割り当てられたオブジェクトの場合、デストラクタから例外をスローするオブジェクトに対して「operator delete」が呼び出されないため、メモリ リークが発生します。驚くべきことに、この場合でも基本クラスのデストラクタが呼び出されます:派生クラスのデストラクタが例外をスローした場合、基本クラスのデストラクタはどうなりますか?
例外がデストラクタ内でキャッチされた場合 (例外がデストラクタを離れないようにするため)、別の例外のスタックの巻き戻しが進行中であっても問題ありません。このケースについては、こちらで詳しく説明しています: http://bin-login.name/ftp/pub/docs/programming_languages/cpp/cffective_cpp/MEC/MI11_FR.HTM
簡単な答えです。dtor からの例外を許可しないでください。
複雑な答え。別の例外がアクティブなときに例外が dtor をエスケープした場合にのみ、本当に釘付けになります。これの通常のケースは、別の例外からスタックを巻き戻し、問題のオブジェクトが破棄された場合です。その場合、例外が dtor をエスケープして呼び出された場合、を呼び出しstd::terminate
て独自のハンドラーを配置できることに注意してください。のデフォルトの実装は、abort を呼び出すことです。std::terminate
std::set_terminate
std::terminate
さらに複雑なことに、例外の安全性を保証したいほとんどの関数 (主に基本保証または強力な保証) は、dtor* をスローしないように、基礎となる型に依存しています。
問題は、このエラーが発生したときにプログラムがどのような状態になるかということです。どうすれば回復できますか?この回復はどこで処理する必要がありますか? 特定のケースを見て、これらの問題を解決する必要があります。例外をキャッチして無視しても問題ない場合があります。また、危険信号を出す必要がある場合もあります。
したがって、答えは次のとおりです。C++ では dtor で例外をスローすることは許可されていますが、エスケープすることは決して許可しないでください。
*例外保証の簡単な概要は次のとおりです (こちらはより長い記事です) 。
- 要約: Abrahams 例外の安全性の保証 (basic、strong、および nothrow) を簡単に定義します。
基本的な保証は、失敗した操作がプログラムの状態を変更する可能性があることですが、リークは発生せず、影響を受けるオブジェクト/モジュールは引き続き破壊可能で使用可能であり、一貫した (ただし必ずしも予測可能であるとは限りません) 状態です。
強力な保証には、トランザクションのコミット/ロールバックのセマンティクスが含まれます。失敗した操作は、操作対象のオブジェクトに関してプログラムの状態が変更されていないことを保証します。これは、オブジェクトに影響を与える副作用がないことを意味します。これには、操作中のコンテナーを指すイテレーターなど、関連するヘルパー オブジェクトの有効性やコンテンツが含まれます。
nothrow 保証は、失敗した操作が発生しないことを意味します。操作は例外をスローしません。