関数 F() の開始時に作成する単純な C++ オブジェクトがあり、オブジェクトのコンストラクターとデストラクターを使用して、一致する 2 つの関数 (OpDo、OpUndo) が F() の開始時と戻り時に確実に呼び出されるようにします。ただし、F() の本体内で例外がスローされた場合に備えて、操作を元に戻したくありません。これはきれいに行うことができますか?std::uncaught-exceptionについて読んだことがありますが、その使用は推奨されていないようです。
3 に答える
ほとんどの人はstd::uncaught_exception()
、例外が保留中かどうかを判断しようとしてきたため、デストラクタがまだない場合はデストラクタから例外をスローできます。それは一般的に、良い考えではないと考えられています。
例外がスローされた場合に操作を元に戻したくない場合は、それでうまくいくはずです。
デストラクタは、オブジェクトが持つリソースを解放する最後のチャンスであることに注意してください。デストラクタが終了すると、オブジェクトは存在しなくなり、オブジェクトが保持していたリソースは永久にリークされるためです。メモリやファイルハンドルなどを割り当てる場合OpDo()
は、デストラクタでそれを処理する必要があります。
スコープ ガードのイディオムを覆すことができます。例外がスローされないときにデストラクタで何もしない代わりに、それを逆にして、例外がスローされない場合にのみ何かを行います。
class DoUndoRAII{
public:
DoUndoRAII()
: noexcept_(false)
{
// your stuff here
}
~DoUndoRAII(){
if(noexcept_){
// do whatever you need to do
}
}
void no_exception(){
noexcept_ = true;
}
private:
bool noexcept_;
};
void func(){
DoUndoRAII do_undo;
// last line
do_undo.no_exception();
}
例外がスローdo_undo.no_exception()
されると、決して呼び出されないため、noexcept_
値が true に設定されることはありません。:) 例はIdeone にあります。
あなたの F がいくつかのクラス Helper を返すと仮定しましょう:
Helper F()
{
MyClass doUndoWrapper;
}
フローが正常な場合 - ヘルパーが作成されます。例外が発生すると、ヘルパーのコピーは作成されません。ヘルパーのプライベート リージョン コンストラクターに配置し、F をフレンドとして宣言することで、このセマンティックを使用してみてください。そのため、誰もヘルパーを作成できません。
class Helper
{
private:
friend Helper F();
Helper(){ //place there OpDo semantic - first entry
// construct this class
Helper(const Helper& copy){ //this must present to allow stack operations
// copy constructor will be called at end of `F` to return value
// so place OpUndo semantic there to mark success without exception