『More Effective C+』まとめノート(二)——異常

4581 ワード

異常
条項9:destructorsによるリソースの漏洩回避
  • この規則を堅持して、リソースをオブジェクト内にカプセル化すれば(スマートポインタshared_ptrのように)、通常、exceptionsの出現時にリソースの漏洩を回避することができる.
  • 簡単に言えば、リソースが関数に異常を投げた場合に解放できない可能性がある場合、リソースをオブジェクト内(RAII)にカプセル化し、オブジェクトの構造関数を利用して自動的にリソースを解放することができ、exceptionsが発生してもリソース漏洩は発生しない.

  • 条項10:constructors内でリソースの漏洩を阻止する(resource leak)
  • C++は、構築されたオブジェクトのみを解析します.オブジェクトはconstructorの実行が完了している場合にのみ完了構造が妥当である.
  • C++は「構築中にexceptionsを投げ出す」オブジェクトを自動的にクリーンアップしないため、constructorsを設計し、その場合も自己クリーンアップできるようにしなければなりません.通常、これは可能なすべてのexceptionsをキャプチャし、クリーンアップを実行し、exceptionを再放出して伝播し続けるだけです.
  • しかし、より優雅な方法は、constructor内で初期化する必要があるオブジェクトをリソースと見なし、スマートポインタに渡して管理することです.
  • 結論:auto_ptr(C++11後にshared_ptrまたはunique_ptr)オブジェクトを使用してpointer class membersの代わりにすると、constructorsの強化工事を行い、「exceptionsが発生したときにリソースが漏れる」という危機を回避し、destructors内で自動的にリソースを解放する必要はありません.const member pointersがnon-const member pointersと同じように優雅な処理を行うことを許可します.

  • 条項11:異常(exceptions)流出destructors外禁止
    Session::Session()
    {
      try {
        logDestruction(this);
      }
      catch (...) {
      }
    }
  • ここのcatch文ブロックは何もしていないように見えますが、外見は人をだましやすいです.この文ブロックは、「logDestructionによって投げ出されたexceptions」がセッションdestructorの外に流れることを阻止する.
  • には、「exceptionsがdestructorsの外に伝わるのを全力で阻止する」という2つの良い理由があります.第一に、terminate関数がexception伝播プロセスのスタック展開(stack-unwinding)メカニズムで呼び出されることを回避することができる.第二に、destructorsが完了すべきすべてのことを完了することを確保するのに役立ちます.

  • 条項12:「exceptionを投げ出す」と「パラメータを渡す」または「虚関数を呼び出す」の違いを理解する
  • 「放出exception」と「伝達パラメータ」の同じ点は、それらの伝達方式は3つある:by value(伝達値)、by reference(伝達参照)、by pointer(伝達ポインタ).しかし、パラメータやexceptionsを渡している場合、発生することはまったく異なる可能性があります.なぜなら、関数を呼び出すと、制御権は最終的に呼び出し側に戻ります(関数が失敗して戻らない限り).しかし、exceptionを投げ出すと、制御権は放出側に戻りません.
  • 「exceptionを放出」と「伝達パラメータ」の違い1:C++特別宣言、1つのオブジェクトがexceptionとして放出されると、常にコピー(copy)が発生します.catch文パラメータの場合by reference、またはオブジェクトを投げ出してstaticと宣言してもコピーが発生します.また、レプリケーション動作は常にオブジェクトの静的タイプを基本とします.
  • 「exception objectsは必ずレプリケーション動作をもたらす」という事実も、「伝達パラメータ」と「exceptionを投げ出す」との間のもう一つの違いを説明している.後者は前者より遅いことが多い.
  • 一般的には、
  • という文を使用する必要があります.
    throw;現在のexceptionを再び投げ出すことができ、その間に伝播されるexceptionのタイプを変える機会はありません.また、新しいexception objectを生成する必要がないため、効率的です.
  • 「exceptionを放出」と「伝達パラメータ」の違い2:関数呼び出し中にnon-const referenceパラメータに一時オブジェクトを伝達する場合は許可されませんが、exceptionは合法です.
  • 「exceptionを投げ出す」と「伝達パラメータ」の違い3:一般的に、関数伝達パラメータを呼び出すときに許容される暗黙的な変換は、「exceptionsとcatch句が一致する」ときに発生しない.
  • 「exceptionsがcatch句に一致する」プロセスでは、2つの変換しか発生しません.1つ目は、「継承アーキテクチャ内のクラス変換」です.2つ目は、1つの「有型ポインタ」から「無型ポインタ」に変更することです(したがって、パラメータはconst void*のcatch句であり、任意のポインタタイプのexceptionをキャプチャできます).
  • 「exceptionを投げ出す」と「伝達パラメータ」の違い4:catch句は常に出現順にマッチングを試みる.虚関数呼び出しと比較して、虚関数は「best fit」(最適一致)ポリシーを採用し、exception処理メカニズムは「first fit」(最初に一致)ポリシーを採用する.したがって、base classに対して設計されたcatch句「derived classに対して設計されたcatch句」の前に絶対に置かないでください.
  • 条項13:by reference方式でexceptionsをキャプチャする
  • by reference方式と比較して、by value方式でexceptionをキャプチャすると、渡されたオブジェクトが2回コピーされ、2つのコピーが生成されます.1つのコンストラクションアクションは、「任意のexceptionsで生成される一時オブジェクト」に使用され、もう1つのコンストラクションアクションは、「一時オブジェクトをcatchのパラメータにコピー」に使用されます.
  • ローカルオブジェクトへのポインタは、exceptionがscopeから送信されたときに破棄されるため、catch句は「破棄されたオブジェクト」へのポインタを取得します.
  • catch by referenceの場合、オブジェクト削除の問題を避けることができます(by pointerが直面します)--それはあなたにすぐに責任を負わせて、しても、しなくてもいいです.また、exception objectsの切断の問題(派生クラスオブジェクトのexception objectsがスナップされ、ベースクラスオブジェクトとみなされるexception objectsは、派生成分を失うことになります.オブジェクト切断の問題は、静的な結合によるもので、参照やポインタを使用しても発生しません).C++標準exceptionsをキャプチャする能力を維持することができます.exception objectsがコピーされる回数も制約されています.

  • 条項14:exception specificationsの賢明な運用
  • exception specificationの例では、intタイプのexceptionsのみを投げ出す関数が
  • と宣言されています.
    void fun() throw(int);
  • 関数がexception specificationに含まれていないexceptionを投げ出すと、このエラーは実行時に検証され、特殊関数unexpectedが自動的に呼び出されます.unexpectedのデフォルトの動作はterminateを呼び出すことです.
  • このunexpectedを回避する方法は、次のとおりです.
  • はtemplatesとexception specificationsを混合して使用するべきではありません.
  • A関数内でB関数が呼び出され、B関数にexception specificationsがない場合、A関数自体もexception specificationsを設定しないでください.
  • は、「システム」が投げ出す可能性のあるexceptions(bad_allocなど)を処理する.

  • C++を使用すると、予想外のexceptionsの代わりに異なるタイプのexceptionsを使用できます.予期しない関数の代替者が現在のexceptionを再放出すると、標準タイプbad_Exceptionに取って代わる.
  • void convertUnexpected()
    {
      throw;
    }
    
    set_unexpected(convertUnexpected);
  • 上記の手配をした場合、各exception specificationsにbad_が含まれています.Exception、またはそのベースクラスでは、プログラムが予想外のexceptionで実行を中止する心配はありません.

  • 例外処理(exception handling)のコストについて
  • exceptionに関するコストを最小化するために、exceptionsをサポートしない限り、コンパイラはサポートしません.try文ブロックとexception specificationsの使用を非使用不可の場所に制限し、本当に異常な場合にexceptionsを投げ出してください.