2011-09-29 17 views
2

私はC++での例外処理の利点を理解しており、扱いにくいかもしれません。ルールの1つは、すべての機能がスローされる可能性があるということです。しかし、関数が投げていないことを確認したい状況があります。このような状況を処理するためのよく知られた慣行やガイドラインを探しています。例:C++の例外処理ガイドライン

try 
{ 
    // do something 
} 
catch (std::runtime_error& error) 
{ 
    save_log (error); 
    emit_dbus_signal (error); 
} 

save_log()またはemit_dbus_signal()が失敗した場合、私は気にしない、私は、私はそれらを呼び出すしようとしたことを、確認します。

ThreadPool thread_pool; 
SocketsPool socket_pool; 
MainLoop main_loop; 

try 
{ 
    thread_pool.init(); 
    socket_pool.init(); 

    main_loop.run(); 
} 
catch (std::runtime_error& error) 
{ 
    save_log (error); 
    emit_dbus_signal (error); 
} 

thread_pool.finalize(); 
socket_pool.finalize(); 

私は私がthread_poolsocket_poolを確定しようとしたことを、確認します。ファイナライズ処理中のエラーは、finalize()メソッド内で処理する必要があります。

私は覚えていますが、どの機能はスローされませんが、小さなプログラムでのみ機能します。 _nothrowのような接尾辞をそのような「非投げ」関数の名前に追加し、コードを書いている間これを処理すべきですか?例外仕様はC++ 11以降で廃止されましたので、避けたいと思います。 noexceptはどうですか?私はこの新機能を理解しているかどうかまだ分かりません。それは私が探しているものですか?

C++ 11ではコンパイル時にチェックがありません。

または、私は完全に絞っていますか? :)

+1

予約の提案:Herb Sutterの "Exceptional C++" –

答えて

3

RAIIを出発点として理解する必要があると思います。

RAIIを正しく使用すると、手動でオブジェクトをファイナライズする必要があると思われるケースのほとんどが魔法のように消えてしまっていることがわかります。そのため、多くの場合、 try/catch/finalizeのアプローチを完全に試してください。上記の場合

他の人が言ったように、あなたはまだどこかにcatch文であなたのsave_log/emit_dbus_signal通話をするつもりだ...

、ThreadPoolののコンストラクタは、initを呼び出します()、およびデストラクタは、ファイナライズを呼ぶだろう()。ファイナライズ機能については

+6

's/catch/finally /' 「キャッチ」はRAIIと一緒に行かない。仮想的な 'finally'ブロックの必要性があります。 –

+0

特定の順序でオブジェクトをファイナライズする必要がある場合はどうすればよいですか? – Goofy

+1

オブジェクトは、作成された順序とは逆の順序で常に破棄されます。 – Alan

0

、あなたが代わりにRAII原理でラッパークラスを使用することを検討すべきである、つまり、使用してコードを書き直す:

try { 
    initialized_thread_pool pool = thread_pool.init(); 
} catch (std::runtime_error& error) { handle(error); } 

とinitialized_thread_poolのデストラクタでファイナライズを行います。

この原則が正しいとすれば、例外仕様はそれほど問題にはならないようです。

0

あなたの関数がスローした場合RAII http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

にまで読むと、その関数内の任意のスタック変数は、まだ彼らのデストラクタが呼び出されます。つまり、~ThreadPool()~SocketPool()デストラクタの内部でfinalize()と呼ぶだけです。

ThreadPoolまたはSocketPoolクラスを変更できない場合は、破棄時にfinalizeを呼び出す独自のラッパークラスを作成できます。その後、

class ScopedThreadPool 
{ 
public: 
    ScopedThreadPool(ThreadPool &threadPool) : m_threadPool(threadPool) {} 
    ~ScopedThreadPool() { m_threadPool.finalize(); } 

private: 
    ThreadPool &m_threadPool; 
}; 

そして...

ThreadPool threadPool; 
ScopedThreadPool scopedThreadPool(threadPool); 

関数が終了後、ScopedThreadPoolデストラクタがあなたのためfinalize()を呼び出します(どちらかreturnまたはthrowを経由して)このようにそれを使用しています。

+1

なぜdownvote? – Alan

3

"記憶"するだけではなく、どの機能がスローされないことが保証されているのは確かです。

一般的な例は、swap関数がno-throwでなければならないということです。その場合、名前にnothrowを置く理由はありません。swapの多くの用途を理解する必要はありません。同様に、log関数が投げることのないプロジェクトのルールを作ることもできます。しかしこれはまだ文書化する必要があります。理想的には、すべての関数は、それがなぜ何をスローするのかを文書化しなければなりませんが、すべての関数がどのレベルの例外保証を提供しているのかを確かめなければなりません。

同じ機能を投げて投げていないバージョンを持っていたら、個人的には区別するためにnothrowを付けてください。それ以外は、コードがどのように見えるかを見てください。 7つの関数を連続して呼び出すコードを書くことができます。これらの関数はすべて、正しいコードである必要はありません。今後の読者は、IDEが役立つものの、実際にはスローしないことを確認するために、各関数の宣言をチェックしてチェックする必要はないでしょう。彼らは避けられれば7つのdocファイルを読む必要はないと確信しています。そのような場合、関数名にハンガリー風の疣贅があると便利だと思いますが、その種のものはすぐに手に入らず、コードを読みにくくするのは簡単ではありません。

また、命名規則を使用すると、演算子のオーバーロードがかなり難しくなります。operator+を名前で区別することはできません。

空の例外の指定はOKで、C++ 11 noexceptがおそらく良いでしょう。コンパイラの意味とは別に、ドキュメントの作成に役立ちます。面倒な例外ではない空のスペックです。

誰もがfinalizeについて言っていることに同意します。それはデストラクタの目的です。

1

これは実際に何をするつもりかによって異なります。あなたがsave_logemit_dbus_signalが失敗するかどうか気にしないと言ったら、が気にしないことを言っていません。を意味します。つまり、save_logが失敗した場合でも、emit_dbus_signalを試してみますか?もしそうなら、次のことができます。

try { 
    try { 
    // current code 
    } catch (std::runtime_error const & error) { 
    // current handling 
    } 
} catch (...) {} // ensure that no other exception escapes either the try or the catch blocks 
thread_pool.finalize(); 
socket_pool.finalize(); 

が実際にあります。

catch (std::runtime_error& error) { 
    try { save_log(error); } catch (...) {} 
    try { emit_dbus_signal(error); } catcn (...) {} 
} 

あなたはおよそemit_dbus_signalsave_logが失敗した場合に呼び出されていない気にしない場合は、別のアプローチは、二try/catchtry/catch全体を包むだろうScopeGuardの行でどのように関数が完了するかにかかわらず、threadpool.finalize()が呼び出されるようにRAIIを使用するような、他の方法があります。

0

throw()を関数宣言の末尾に追加します。関数が例外をスローした場合、コンパイラ(少なくともgccは)が文句を言うでしょう。

void function() throw(); 

これは、この関数が例外をスローしないことを示す標準のC++です。以前は、どの例外がスローされたのかを言うことができましたが、C++ 11はその機能を削除したと思います。上記のように、空の節だけが残ります。