回答:
いいえ、C ++は 'finally'ブロックをサポートしていません。その理由は、C ++が代わりにRAIIをサポートしているためです。「リソースの取得は初期化です」- 本当に有用な概念の悪い名前†。
アイデアは、オブジェクトのデストラクタがリソースの解放を担当するというものです。オブジェクトに自動ストレージ期間がある場合、オブジェクトのデストラクタは、それが作成されたブロックが存在するときに呼び出されます。例外が存在する場合でも、そのブロックが存在する場合でも同様です。これは、Bjarne Stroustrupによるトピックの説明です。
RAIIの一般的な用途は、ミューテックスのロックです。
// A class with implements RAII
class lock
{
mutex &m_;
public:
lock(mutex &m)
: m_(m)
{
m.acquire();
}
~lock()
{
m_.release();
}
};
// A class which uses 'mutex' and 'lock' objects
class foo
{
mutex mutex_; // mutex for locking 'foo' object
public:
void bar()
{
lock scopeLock(mutex_); // lock object.
foobar(); // an operation which may throw an exception
// scopeLock will be destructed even if an exception
// occurs, which will release the mutex and allow
// other functions to lock the object and run.
}
};
RAIIは、オブジェクトを他のクラスのメンバーとして使用することも簡単にします。所有するクラスが破棄されると、RAII管理クラスのデストラクタが結果として呼び出されるため、RAIIクラスによって管理されるリソースが解放されます。つまり、リソースを管理するクラスのすべてのメンバーにRAIIを使用すると、メンバーのリソースの有効期間を手動で管理する必要がないため、オーナークラスの非常にシンプルな、おそらくデフォルトのデストラクタを使用して問題を回避できます。 。(これを指摘してくれたMike Bに感謝します。)
C#またはVB.NETを使い慣れている方は、RAIIがIDisposableおよび 'using'ステートメントを使用した.NETの確定的破壊に似ていることに気付くかもしれません。実際、2つの方法は非常によく似ています。主な違いは、RAIIがメモリを含むすべてのタイプのリソースを確定的に解放することです。.NETでIDisposableを実装すると(.NET言語のC ++ / CLIでも)、メモリを除いてリソースが確定的に解放されます。.NETでは、メモリは確定的に解放されません。メモリはガベージコレクションサイクル中にのみ解放されます。
†一部の人々は、「破壊は資源放棄である」がRAIIイディオムのより正確な名前であると信じています。
C ++では、RAIIのため、finallyは必要ありません。
RAIIは、例外安全の責任をオブジェクトのユーザーからオブジェクトの設計者(および実装者)に移します。(設計/実装で)例外の安全性を1度正しく修正するだけでよいので、これは正しい場所だと主張します。最終的に使用することにより、オブジェクトを使用するたびに例外の安全性を正しく取得する必要があります。
また、IMOのコードはきれいに見えます(以下を参照)。
例:
データベースオブジェクト。DB接続が使用されていることを確認するには、DB接続を開いて閉じる必要があります。RAIIを使用することにより、これはコンストラクター/デストラクターで行うことができます。
void someFunc()
{
DB db("DBDesciptionString");
// Use the db object.
} // db goes out of scope and destructor closes the connection.
// This happens even in the presence of exceptions.
RAIIを使用すると、DBオブジェクトを正しく簡単に使用できます。DBオブジェクトは、デストラクタを使用してどのように使用したとしても、それ自体を正しく閉じます。
void someFunc()
{
DB db = new DB("DBDesciptionString");
try
{
// Use the db object.
}
finally
{
// Can not rely on finaliser.
// So we must explicitly close the connection.
try
{
db.close();
}
catch(Throwable e)
{
/* Ignore */
// Make sure not to throw exception if one is already propagating.
}
}
}
最終的に使用する場合、オブジェクトの正しい使用はオブジェクトのユーザーに委任されます。すなわち、DB接続を明示的に閉じるのは、オブジェクトユーザーの責任です。これはファイナライザーで実行できると主張できますが、リソースの可用性やその他の制約が制限されている可能性があるため、通常はオブジェクトのリリースを制御し、ガベージコレクターの非決定的な動作に依存しないようにします。
これも簡単な例です。
複数のリソースを解放する必要がある場合、コードは複雑になる可能性があります。
より詳細な分析はここで見つけることができます:http : //accu.org/index.php/journals/236
// Make sure not to throw exception if one is already propagating.
このため、C ++デストラクタでも例外をスローしないことが重要です。
通常、RAIIの方が優れていますが、C ++でfinallyセマンティクスを簡単に使用できます。少量のコードを使用する。
その上、C ++ コアガイドラインが最終的に与えます。
これは、GSL Microsoft実装へのリンクとMartin Moene実装へのリンクです。
Bjarne Stroustrupは、GSLにあるすべてのものは最終的には標準に入るつもりであると何度も言っています。したがって、finallyを将来的に使用できるようにする必要があります。
ただし、必要に応じて簡単に実装できます。
C ++ 11では、RAIIとラムダにより、最終的に将軍を作成できます。
namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
FinalAction(F f) : clean_{f} {}
~FinalAction() { if(enabled_) clean_(); }
void disable() { enabled_ = false; };
private:
F clean_;
bool enabled_{true}; }; }
template <typename F>
detail::FinalAction<F> finally(F f) {
return detail::FinalAction<F>(f); }
使用例:
#include <iostream>
int main() {
int* a = new int;
auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
std::cout << "doing something ...\n"; }
出力は次のようになります。
doing something...
leaving the block, deleting a!
個人的には、C ++プログラムでPOSIXファイル記述子を確実に閉じるために、これを数回使用しました。
リソースを管理し、あらゆる種類のリークを回避する実際のクラスを用意することは、通常はより良い方法ですが、クラスを作りすぎると聞こえる場合に、これは最終的に役立ちます。
また、私のようなそれより良い他の言語よりも最終的には自然に使用している場合ので、あなたは(私の例では、開口部コード近くの決算コードを書く新しいおよび削除)C ++でいつものようにLIFO順で破壊下記の建設を。唯一の欠点は、実際には使用しない自動変数を取得し、ラムダ構文によって少しうるさくなることです(私の例では、4行目の例では、最終的に単語というだけで、右側の{}ブロックは意味があります。残りは本質的にノイズです)。
もう一つの例:
[...]
auto precision = std::cout.precision();
auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
std::cout << std::setprecision(3);
無効ならばメンバーは便利です最終的には唯一の障害が発生した場合に呼び出されることがあります。たとえば、3つの異なるコンテナにオブジェクトをコピーする必要がある場合、finallyを設定して、各コピーを元に戻し、すべてのコピーが成功した後に無効にすることができます。そうすることで、破壊がスローできない場合、強力な保証が保証されます。
例を無効にする:
//strong guarantee
void copy_to_all(BIGobj const& a) {
first_.push_back(a);
auto undo_first_push = finally([first_&] { first_.pop_back(); });
second_.push_back(a);
auto undo_second_push = finally([second_&] { second_.pop_back(); });
third_.push_back(a);
//no necessary, put just to make easier to add containers in the future
auto undo_third_push = finally([third_&] { third_.pop_back(); });
undo_first_push.disable();
undo_second_push.disable();
undo_third_push.disable(); }
C ++ 11を使用できない場合でも、finallyを使用できますが、コードは少し長くなります。コンストラクタとデストラクタのみで構造体を定義するだけで、コンストラクタは必要なものへの参照を取得し、デストラクタが必要なアクションを実行します。これは基本的に手動で行われるラムダの動作です。
#include <iostream>
int main() {
int* a = new int;
struct Delete_a_t {
Delete_a_t(int* p) : p_(p) {}
~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
int* p_;
} delete_a(a);
std::cout << "doing something ...\n"; }
FinalAction
基本的に人気が同じであるScopeGuard
だけ別の名前で、イディオム。
RAIIは、スタックベースのオブジェクトでクリーンアップを簡単にするだけでなく、オブジェクトが別のクラスのメンバーである場合にも同じ「自動」クリーンアップが発生するため便利です。所有クラスが破棄されると、結果としてそのクラスのdtorが呼び出されるため、RAIIクラスによって管理されるリソースがクリーンアップされます。
つまり、RAIIニルバーナに到達し、クラスのすべてのメンバーがRAII(スマートポインターのような)を使用する場合、所有者クラスは手動で管理する必要がないため、非常にシンプルな(おそらくデフォルトの)dtorで済むことができます。メンバーリソースのライフタイム。
とにかくガベージコレクターによってリソースが自動的に割り当て解除されているにもかかわらず、マネージ言語でさえ最終ブロックを提供するのはなぜですか?
実際、ガベージコレクターに基づく言語には、「最終的に」さらに多くのものが必要です。ガベージコレクターはオブジェクトを適時に破棄しません。そのため、メモリ以外の問題を正しくクリーンアップすることはできません。
動的に割り当てられたデータに関しては、スマートポインターを使用する必要があると多くの人が主張します。
しかしながら...
RAIIは例外の安全性の責任をオブジェクトのユーザーからデザイナーに移します
悲しいことに、これはそれ自体の没落です。古いCプログラミングの習慣はひどく死にます。Cまたは非常にCスタイルで記述されたライブラリを使用している場合、RAIIは使用されません。APIフロントエンド全体を書き直すのではなく、それはまさにあなたが作業しなければならないことです。 次に、「最終的に」の欠如は本当に噛み付きます。
CleanupFailedException
。RAIIを使用してそのような結果を達成するもっともらしい方法はありますか?
SomeObject.DoSomething()
メソッドを呼び出して、それが(1)成功したか、(2)副作用なしで失敗したか、(3)呼び出し側が対処する準備ができた副作用で失敗したかどうかを知りたい場合が多くあります。、または(4)呼び出し元が対処できない副作用により失敗した。呼び出し側だけが、対処できる状況と対処できない状況を認識します。発信者に必要なのは、状況を知る方法です。例外に関する最も重要な情報を提供するための標準的なメカニズムがないのは残念です。
C ++ 11ラムダ関数を使用したもう1つの「最後の」ブロックエミュレーション
template <typename TCode, typename TFinallyCode>
inline void with_finally(const TCode &code, const TFinallyCode &finally_code)
{
try
{
code();
}
catch (...)
{
try
{
finally_code();
}
catch (...) // Maybe stupid check that finally_code mustn't throw.
{
std::terminate();
}
throw;
}
finally_code();
}
コンパイラが上記のコードを最適化することを期待しましょう。
これで、次のようなコードを記述できます。
with_finally(
[&]()
{
try
{
// Doing some stuff that may throw an exception
}
catch (const exception1 &)
{
// Handling first class of exceptions
}
catch (const exception2 &)
{
// Handling another class of exceptions
}
// Some classes of exceptions can be still unhandled
},
[&]() // finally
{
// This code will be executed in all three cases:
// 1) exception was not thrown at all
// 2) exception was handled by one of the "catch" blocks above
// 3) exception was not handled by any of the "catch" block above
}
);
必要に応じて、このイディオムを「try-finally」マクロにラップできます。
// Please never throw exception below. It is needed to avoid a compilation error
// in the case when we use "begin_try ... finally" without any "catch" block.
class never_thrown_exception {};
#define begin_try with_finally([&](){ try
#define finally catch(never_thrown_exception){throw;} },[&]()
#define end_try ) // sorry for "pascalish" style :(
「最終的に」ブロックがC ++ 11で利用可能になりました。
begin_try
{
// A code that may throw
}
catch (const some_exception &)
{
// Handling some exceptions
}
finally
{
// A code that is always executed
}
end_try; // Sorry again for this ugly thing
個人的には、「マクロ」バージョンの「finally」イディオムは好きではありません。その場合、構文がよりかさばりますが、純粋な「with_finally」関数を使用することを好みます。
上記のコードをここでテストできます:http : //coliru.stacked-crooked.com/a/1d88f64cb27b3813
PS
コードで最終的にブロックが必要な場合は、スコープ付きガードまたはON_FINALLY / ON_EXCEPTIONマクロがおそらくニーズによりよく適合します。
ON_FINALLY / ON_EXCEPTIONの使用例を次に示します。
void function(std::vector<const char*> &vector)
{
int *arr1 = (int*)malloc(800*sizeof(int));
if (!arr1) { throw "cannot malloc arr1"; }
ON_FINALLY({ free(arr1); });
int *arr2 = (int*)malloc(900*sizeof(int));
if (!arr2) { throw "cannot malloc arr2"; }
ON_FINALLY({ free(arr2); });
vector.push_back("good");
ON_EXCEPTION({ vector.pop_back(); });
...
このような古いスレッドを掘り下げて申し訳ありませんが、次の理由には大きなエラーがあります:
RAIIは、例外安全の責任をオブジェクトのユーザーからオブジェクトの設計者(および実装者)に移します。(設計/実装で)例外の安全性を1度正しく修正するだけでよいので、これは正しい場所だと主張します。最終的に使用することにより、オブジェクトを使用するたびに例外の安全性を正しく取得する必要があります。
多くの場合、動的に割り当てられたオブジェクト、オブジェクトの動的な数などを処理する必要があります。tryブロック内で、一部のコードは多数のオブジェクト(実行時に決定される数)を作成し、それらへのポインターをリストに格納します。現在、これは珍しいシナリオではありませんが、非常に一般的です。この場合、次のようなものを書きたいと思います
void DoStuff(vector<string> input)
{
list<Foo*> myList;
try
{
for (int i = 0; i < input.size(); ++i)
{
Foo* tmp = new Foo(input[i]);
if (!tmp)
throw;
myList.push_back(tmp);
}
DoSomeStuff(myList);
}
finally
{
while (!myList.empty())
{
delete myList.back();
myList.pop_back();
}
}
}
もちろん、リスト自体はスコープ外に出ると破棄されますが、作成した一時オブジェクトはクリーンアップされません。
代わりに、醜いルートをたどる必要があります。
void DoStuff(vector<string> input)
{
list<Foo*> myList;
try
{
for (int i = 0; i < input.size(); ++i)
{
Foo* tmp = new Foo(input[i]);
if (!tmp)
throw;
myList.push_back(tmp);
}
DoSomeStuff(myList);
}
catch(...)
{
}
while (!myList.empty())
{
delete myList.back();
myList.pop_back();
}
}
また、とにかくガベージコレクターによってリソースが自動的に割り当て解除されているにもかかわらず、マネージ言語でさえ最終ブロックを提供するのはなぜですか?
ヒント:メモリの割り当てを解除するだけでなく、「最終的に」実行できることは他にもあります。
new
NULLを返さず、代わりに例外をスローします
std::shared_ptr
、およびstd::unique_ptr
直接STDLIBです。
FWIW、Microsoft Visual C ++は、最終的にtryをサポートします。これは、MFCアプリで、クラッシュの原因となる深刻な例外をキャッチする方法として歴史的に使用されてきました。例えば;
int CMyApp::Run()
{
__try
{
int i = CWinApp::Run();
m_Exitok = MAGIC_EXIT_NO;
return i;
}
__finally
{
if (m_Exitok != MAGIC_EXIT_NO)
FaultHandler();
}
}
私は過去にこれを使って、終了する前に開いているファイルのバックアップを保存するようなことをしました。ただし、特定のJITデバッグ設定はこのメカニズムを壊します。
他の回答で指摘されているように、C ++はのfinally
ような機能をサポートできます。この機能の実装は、おそらく標準言語の一部に最も近いものであり、Bjarne StoustrupとHerb Sutterによって編集されたC ++を使用するための一連のベストプラクティスであるC ++ Core Guidelinesに付随するものです。の実装はfinally
、ガイドラインサポートライブラリ(GSL)の一部です。ガイドライン全体を通して、finally
古いスタイルのインターフェースを扱う場合は、の使用をお勧めします。また、適切なリソースハンドルが利用できない場合は、「final_actionオブジェクトを使用してクリーンアップを表現する」というタイトルの独自のガイドラインもあります。。。
したがって、C ++だけでなく、 finally
、実際に多くの一般的なユースケースで使用することをお勧めします。
GSL実装の使用例は次のようになります。
#include <gsl/gsl_util.h>
void example()
{
int handle = get_some_resource();
auto handle_clean = gsl::finally([&handle] { clean_that_resource(handle); });
// Do a lot of stuff, return early and throw exceptions.
// clean_that_resource will always get called.
}
GSLの実装と使用法は、Paolo.Bolzoniの回答と非常によく似ています。1つの違いは、によって作成されたオブジェクトにgsl::finally()
はdisable()
呼び出しがないことです。その機能が必要な場合(たとえば、リソースがアセンブルされ、例外が発生しないことになったらリソースを返すため)、Paoloの実装を好むかもしれません。それ以外の場合、GSLの使用は、標準化された機能を使用するのと同じくらい簡単です。
実際にはそうではありませんが、たとえば、次のように、それらをある程度エミュレートできます。
int * array = new int[10000000];
try {
// Some code that can throw exceptions
// ...
throw std::exception();
// ...
} catch (...) {
// The finally-block (if an exception is thrown)
delete[] array;
// re-throw the exception.
throw;
}
// The finally-block (if no exception was thrown)
delete[] array;
元の例外が再スローされる前に、finallyブロック自体が例外をスローし、それによって元の例外を破棄する可能性があることに注意してください。これは、Javaのfinal-blockとまったく同じ動作です。また、return
try&catchブロック内では使用できません。
std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e);
finally
ブロック内から例外をスローできるようにする必要があります。
ほぼ Java のキーワードのようfinally
に使用できるマクロを思いつきました。これは、ラムダ関数などのフレンドを使用するため、それ以上が必要です。また、clangでもサポートされている複合ステートメント式の GCC拡張を利用します。finally
std::exception_ptr
std::promise
C++11
警告:この回答の以前のバージョンでは、コンセプトの異なる実装を使用しましたが、さらに多くの制限がありました。
まず、ヘルパークラスを定義しましょう。
#include <future>
template <typename Fun>
class FinallyHelper {
template <typename T> struct TypeWrapper {};
using Return = typename std::result_of<Fun()>::type;
public:
FinallyHelper(Fun body) {
try {
execute(TypeWrapper<Return>(), body);
}
catch(...) {
m_promise.set_exception(std::current_exception());
}
}
Return get() {
return m_promise.get_future().get();
}
private:
template <typename T>
void execute(T, Fun body) {
m_promise.set_value(body());
}
void execute(TypeWrapper<void>, Fun body) {
body();
}
std::promise<Return> m_promise;
};
template <typename Fun>
FinallyHelper<Fun> make_finally_helper(Fun body) {
return FinallyHelper<Fun>(body);
}
次に、実際のマクロがあります。
#define try_with_finally for(auto __finally_helper = make_finally_helper([&] { try
#define finally }); \
true; \
({return __finally_helper.get();})) \
/***/
次のように使用できます。
void test() {
try_with_finally {
raise_exception();
}
catch(const my_exception1&) {
/*...*/
}
catch(const my_exception2&) {
/*...*/
}
finally {
clean_it_all_up();
}
}
を使用std::promise
すると、実装が非常に簡単になりますが、おそらく、不要なオーバーヘッドもかなり導入されるため、から必要な機能のみを再実装することで回避できますstd::promise
。
¹ 警告:のJavaバージョンのように動作しないことがいくつかありfinally
ます。私の頭の上から:
break
内からのステートメントで外側のループから抜けることはできませんtry
catch()
彼らはラムダ関数内で生きているので、のブロック。catch()
後に少なくとも1つのブロックが必要ですtry
必要です。これはC ++の要件です。try
and catch()'s
ブロック内に戻り値がない場合、finally
マクロはを返すコードに展開されるため、コンパイルは失敗しますvoid
。これは間違いかもしれませんfinally_noreturn
一種のマクロを持つことによって無効にます。全体として、私がこれを自分で使用するかどうかはわかりませんが、それを使って遊ぶのは楽しかったです。:)
catch(xxx) {}
の先頭に不可能なブロックを置くだけで警告2を取り除くことができますfinally
。xxxは、少なくとも1つのcatchブロックを持たせるためだけの偽のタイプです。
catch(...)
なくなりますね。
xxx
決して使用されないプライベート名前空間で、あいまいなタイプを構成するだけです。
私がすべきだと思うユースケースがありますfinally
、私はビューの流れの観点から読みやすいと思いとして、C ++ 11言語の完全に受け入れ一部では。私のユースケースは、消費者/生産者のスレッドチェーンです。nullptr
すべてのスレッドをシャットダウンするために実行の最後にが送信されます。
C ++でサポートされている場合は、コードを次のようにする必要があります。
extern Queue downstream, upstream;
int Example()
{
try
{
while(!ExitRequested())
{
X* x = upstream.pop();
if (!x) break;
x->doSomething();
downstream.push(x);
}
}
finally {
downstream.push(nullptr);
}
}
ループが終了した後に発生するため、これは最終的に宣言をループの先頭に置くよりも論理的だと思いますが、C ++では実行できないため、これは希望的な考えです。キューdownstream
は別のスレッドに接続されているので、この時点では破棄できないためpush(nullptr)
、デストラクタにセンチネルを入れるdownstream
ことはできません。他のスレッドが受信するまで生き続ける必要があります。nullptr
。ます。
したがって、これはラムダでRAIIクラスを使用して同じことを行う方法です。
class Finally
{
public:
Finally(std::function<void(void)> callback) : callback_(callback)
{
}
~Finally()
{
callback_();
}
std::function<void(void)> callback_;
};
そしてこれがあなたの使い方です:
extern Queue downstream, upstream;
int Example()
{
Finally atEnd([](){
downstream.push(nullptr);
});
while(!ExitRequested())
{
X* x = upstream.pop();
if (!x) break;
x->doSomething();
downstream.push(x);
}
}
多くの人が述べたように、解決策は、C ++ 11機能を使用して、finallyブロックを回避することです。機能の1つはunique_ptr
です。
これは、RAIIパターンを使用して書かれたMephaneの回答です。
#include <vector>
#include <memory>
#include <list>
using namespace std;
class Foo
{
...
};
void DoStuff(vector<string> input)
{
list<unique_ptr<Foo> > myList;
for (int i = 0; i < input.size(); ++i)
{
myList.push_back(unique_ptr<Foo>(new Foo(input[i])));
}
DoSomeStuff(myList);
}
C ++標準ライブラリコンテナでのunique_ptrの使用に関するいくつかの紹介はこちらです
代替案を提供したいと思います。
最終的にブロックが常に呼び出されるようにしたい場合は、最後のcatchブロックの後に置くだけです(これはおそらくcatch( ... )
不明な例外をキャッチするためのものです)
try{
// something that might throw exception
} catch( ... ){
// what to do with uknown exception
}
//final code to be called always,
//don't forget that it might throw some exception too
doSomeCleanUp();
例外がスローされたときに最後にブロックする必要がある場合は、ブールローカル変数を使用できます。実行する前にfalseに設定し、tryブロックの最後にtrue割り当てを配置してから、catchブロックの後に変数をチェックします。値:
bool generalAppState = false;
try{
// something that might throw exception
//the very end of try block:
generalAppState = true;
} catch( ... ){
// what to do with uknown exception
}
//final code to be called only when exception was thrown,
//don't forget that it might throw some exception too
if( !generalAppState ){
doSomeCleanUpOfDirtyEnd();
}
//final code to be called only when no exception is thrown
//don't forget that it might throw some exception too
else{
cleanEnd();
}
また、RIIAは、例外処理や最終的な機能の代わりとして完全に役立つものではないと思います。ところで、私はまた、RIIAは悪名高いと思います。私はこれらのタイプのクラスを「管理人」と呼び、それらをLOTとして使用します。リソースの初期化も取得もしていない時間の95%は、スコープベースで変更を適用するか、すでにセットアップされているものを取り、それが確実に破棄されるようにします。これは、インターネットに取り憑かれた公式のパターン名であるため、自分の名前の方がいいかもしれないと提案されても悪用されます。
複数をキャッチする必要がある場合にすべてをクリーンアップするときに複雑さを回避するために、いくつかのアドホックリストの複雑な設定すべてに、それを含むようにクラスを作成する必要があることを要求するのが妥当だとは思わないプロセスで問題が発生した場合の例外タイプ。これは、他の方法では必要のない多くのアドホッククラスにつながります。
はい、特定のリソースを管理するように設計されているクラスや、類似したリソースのセットを処理するように設計されている一般的なクラスは問題ありません。しかし、関係するすべてのものにそのようなラッパーがある場合でも、クリーンアップの調整は、デストラクターの逆順の呼び出しだけではない場合があります。
C ++がfinalを持つことは完全に理にかなっていると思います。つまり、ジーズ、過去10年間に多くの小片やボブがそれにくっついて、奇妙な人々が最後に何かのように突然保守的になり、非常に便利でおそらく他のいくつかのものほど複雑ではないように思える追加されました(ただし、これは私の推測です)。
try
{
...
goto finally;
}
catch(...)
{
...
goto finally;
}
finally:
{
...
}
finally
ません。