クラスの ~dtor 内で保存関数 (ala boost-serialize) を呼び出すと、どのような否定的/未定義の動作が発生する可能性がありますか?
4 に答える
懸念事項が 2 つあります。一方は他方の結果です。
1) デストラクタをエスケープする例外を許可しないでください。その場合、デストラクタがスタックの巻き戻しの一部として呼び出されている場合、ランタイムはterminate()
プログラムを実行します。これは未定義の動作ではありませんが、かなりネガティブです。
このため (もちろん、デストラクタが値を返さないため):
2)デストラクタが成功または失敗を示す合理的な方法はありません(「合理的な」意味で、何らかの別のエラー報告システムを構築する必要はありません)。クラスのユーザーは保存が行われたかどうかを知りたがる可能性があるため、できれば実用的な API を使用して、デストラクタは「ベスト エフォート」ベースでのみデータを保存できることを意味します。保存に失敗した場合でもオブジェクトは破棄されるため、おそらくそのデータは失われます。
このような状況には、ファイル ストリームなどで使用される戦略があります。それはこのように動作します:
- データを保存する
flush()
(またはあなたの場合は)関数がありますsave()
- オブジェクトがまだ保存/フラッシュされていない場合は、デストラクタからこの関数を呼び出します (または、無条件に呼び出しますが、実際の作業を行う必要があるかどうかを関数自体に認識させます)。ファイル ストリームの場合、これは
close()
. スローできる例外をキャッチし、エラーを無視します。
そうすれば、保存が成功したかどうかを知る必要があるユーザーは、電話save()
で確認できます。気にしないユーザー (または、例外がスローされ、スタックの巻き戻しの一部としてオブジェクトが破棄された場合に、可能であれば成功することを気にしないユーザー) は、デストラクタを試すことができます。
つまり、デストラクタは、失敗する可能性があることを最後の手段として試みることができますが、成功または失敗を通知する方法で、ユーザーが同じことを「適切に」行うための手段をさらに提供する必要があります。
はい、これは付随的に、データが書き込まれたかどうかを知る方法がないため、ストリームをフラッシュせずにストリームの状態をチェックし、ストリームの状態をチェックして「適切に」使用していないことを意味します。しかし、それで十分な状況もあり、同じ種類の状況では、クラスがデストラクタに保存するだけで十分な場合もあります。
問題はboost-serialize
、例外をスローできることです。つまり、例外が伝播しているためにデストラクタが呼び出され、巻き戻し時にスタックをクリーンアップしている場合、オブジェクトのデストラクタが別の例外をスローすると、アプリケーションは終了します。
要約すると、一度に伝播する例外は常に 1 つだけです。複数になると、アプリケーションが終了し、例外の目的が無効になります。
それは悪い考えです。
- デストラクタは決して をスローすべきではありません。IO が成功するかどうかは基本的に制御できないため、IO 操作はスローする可能性が非常に高くなります。
- 少なくとも私にとっては、非常に直感的ではありません
。1 つは、そのタイプのすべてのオブジェクトがシリアライズされることを保証することです (デストラクタがそれを防ぐためのチェックを持っていない限り)
b. デストラクタにはクリーンアップという非常に明確な目的があり、データの保存は基本的にクリーンアップの反対です。
デストラクタでシリアル化することによって何を得る必要があるかということです。
RAII を使用している場合、例外があってもシリアライゼーションが実行されることがわかっています。ただし、デストラクタが実行されても、シリアル化がスローされるため実行されることを保証できないため (この場合は少なくとも)、これはあまり利点ではありません。また、障害を適切に処理する能力の多くを失います。