web-dev-qa-db-ja.com

関数tryブロックはいつ役に立ちますか?

プログラマーがいつ関数tryブロックを使用するのか疑問に思います。いつ役に立ちますか?

void f(int i)
try
{
   if ( i  < 0 ) 
      throw "less than zero";
   std::cout << "greater than zero" << std::endl;
}
catch(const char* e)
{
    std::cout << e << std::endl;
}

int main() {
        f(1);
        f(-1);
        return 0;
}

出力:( ideone

greater than zero
less than zero

編集:関数定義の構文が間違っていると思う人もいるかもしれませんが(構文が見慣れないため)、間違いではないと言わざるを得ません。その呼ばれるfunction-try-block。 C++標準の§8.4/ 1 [dcl.fct.def]を参照してください。

39
Nawaz

イニシャライザからのエラーをキャッチするためにコンストラクタで使用します。通常、これらのエラーは検出されないため、これは非常に例外的な使用法です。

そうでなければ、それは役に立たない:私が間違っていると証明されない限り、

void f() try { ... } catch (...) { ... }

厳密に同等です

void f() { try { ... } catch (...) { ... } }
28
Alexandre C.

関数tryブロックは、2つのコンテキストで役立ちます。

a)main()の周りにcatchall句を設定して、ローカルエラー処理について心配することなく小さなユーティリティを記述できるようにする:

_int main()
try {
    // ...
    return 0;
}
catch (...) {
    // handle errors
    return -1;
}
_

これは明らかに、main()自体の内部にtry/catchを持つための単なる構文糖衣です。

b)基本クラスコンストラクターによってスローされた例外を処理するには:

_struct B {
     B() { /*might throw*/ }
};

struct A : B {
     A() 
     try : B() { 
         // ... 
     } 
     catch (...) {
         // handle exceptions thrown from inside A() or by B() 
     } 
};
_
12
hkaiser

上記の機能的な使用法とは別に、function-try-blockを使用して、1レベルのインデントを節約できます。 (Ack、コーディングスタイルについての答え!)

通常、次のようなfunction-try-blockの例が表示されます。

void f(/*...*/)
try {
   /*...*/
}
catch(/*...*/) {
    /*...*/
}

関数スコープがfunction-try-blockがない場合と同じレベルにインデントされている場合。これは、次の場合に役立ちます。

  • 80文字の列制限があり、余分なインデントを指定して行を折り返す必要があります。
  • try catchを使用して既存の関数を改良しようとしていて、関数のすべての行に触れたくない場合。 (ええ、git blame -wを使用することもできます。)

ただし、function-try-blockで完全にラップされている関数の場合は、not function-try-blocksを使用する関数と、同じコードベース内にない関数を交互に使用することをお勧めします。一貫性は、おそらく行の折り返しの問題よりも重要です。 :)

9
kalaxy

コンストラクターの初期化子からの例外をキャッチしたい場合に役立つことがあります。

ただし、そのようにコンストラクターで例外をキャッチした場合は、haveで再スローするか、新しい例外をスローします(つまり、通常はコンストラクターから戻ることはできません)。再スローしない場合、それは暗黙的に発生します。

#include <iostream>

class A
{
public:
  A()
  try {
    throw 5;
  }
  catch (int) {
    std::cout << "exception thrown\n";
    //return; <- invalid
  }
};

int main()
{
  try {
    A a;
  }
  catch (...) {
    std::cout << "was rethrown";
  }
}
6
Vitus

関数tryブロックの動作に関する注意:

  • コンストラクターの場合、関数tryブロックには、データメンバーと基本クラスの構築が含まれます。

  • デストラクタの場合、関数tryブロックには、データメンバーと基本クラスの破棄が含まれます。複雑になりますが、C++ 11の場合、デストラクタ(またはベース/メンバークラスの宣言)の宣言にnoexcept(false)を含める必要があります。そうしないと、破棄の例外が発生すると、キャッチブロック。 returnステートメントをcatchブロックに配置することで、これを防ぐことができる場合があります(ただし、これはコンストラクターでは機能しません)。

  • コンストラクタまたはデストラクタのcatchブロックは、何らかの例外をスローする必要があります (または、キャッチされた例外を暗黙的に再スローします) 。単にreturnすることは合法ではありません(少なくともコンストラクターの関数catchブロックでは)。ただし、exit()などを呼び出すことができることに注意してください。これは、状況によっては意味がある場合があります。

  • Catchブロックは値を返すことができないため、void以外を返す関数では機能しません(プログラムをexit()などで意図的に終了しない限り)。少なくともそれは 私が読んだもの です。

  • コンストラクター関数トライのcatchブロックは、1)構築されていないか、2)catchの前に破棄されているため、データベース/ベースメンバーを参照できません。そのため、関数tryブロックは、オブジェクトの内部状態をクリーンアップするのに役立ちません。オブジェクトは、そこに到達するまでにすでに完全に「デッド」になっているはずです。 この事実により、コンストラクターで関数tryブロックを使用することは非常に危険です。これは、コンパイラーがたまたまフラグを立てない場合、このルールを長期にわたって監視することが難しいためです。

有効な(合法的な)使用

  • コンストラクターまたはそのベース/メンバーコンストラクター中にスローされた例外を(別のタイプ/メッセージに)変換します。
  • 変換または吸収し、デストラクタまたはそのベース/メンバーデストラクタ中に例外がスローされます( デストラクタエチケット にもかかわらず)。
  • プログラムを終了します(おそらく有用なメッセージが表示されます)。
  • ある種の例外ロギングスキーム。
  • 完全にカプセル化されたtry/catchブロックをたまたま必要とするvoidを返す関数のシンタックスシュガー。
3
nobar

それらを使用できるもう1つのことは、完成したビルドに干渉しない方法で、デバッグ中に追加のデータを提供することです。他の誰かがそれを使用したり支持したりするのを見たことがありませんが、それは私が便利だと思うものです。

// Function signature helper.
#if defined(_WIN32) || defined(_WIN64)
    #define FUNC_SIG __FUNCSIG__
#Elif defined(__unix__)
    #define FUNC_SIG __PRETTY_FUNCTION__
// Add other compiler equivalents here.
#endif  /* Function signature helper. */

void foo(/* whatever */)
#ifdef     DEBUG
try
#endif  /* DEBUG */
{
    // ...
}
#ifdef     DEBUG
catch(SomeExceptionOrOther& e) {
    std::cout << "Exception " << e.what() << std::endl
              << "* In function: " << FUNC_SIG << std::endl
              << "* With parameters: " << /* output parameters */ << std::endl
              << "* With internal variables: " << /* output vars */ << std::endl;

    throw;
}
#endif  /* DEBUG */

これにより、コードのテスト中に有用な情報を取得し、何にも影響を与えることなく簡単にダミー化することができます。