2

そのため、C# コード コントラクトと同様に、スコープ ガードまたは同様のメカニズムを使用して、着信/発信オブジェクトの有効性や内部状態の不変性を確保する方法を調査しています。

通常の処理中に予期しない条件/例外が発生し、一部のオブジェクトが一貫性のない状態のままになる特定のケースでは、スコープガードが文句を言うという事実を回避するためにどのメカニズムを使用できるか/使用する必要がありますか?関数から飛び出すときは?

私の要点を説明するためのサンプル疑似コードを次に示します。

struct IObjectValidator;

struct ObjectValidatorScopeGuard
{
  ObjectValidatorScopeGuard(IObjectValidator * pObj) 
    : m_ptr(pObj) 
  {
    Assert(!m_ptr || m_ptr->isValid());
  }

  ~ObjectValidatorScopeGuard()
  {
    Assert(!m_ptr || m_ptr->isValid());
  }
  private:
    IObjectValidtor * m_ptr;
};


int SomeComponent::CriticalMethod(const ThingA& in, ThingB& inout, ThingC * out)
{
  ObjectValidatorScopeGuard sg1(static_cast<IObjectValidator *>(&in));   
  ObjectValidatorScopeGuard sg2(static_cast<IObjectValidator *>(&inout));
  ObjectValidatorScopeGuard sg3(static_cast<IObjectValidator *>(out));

  // create out
  try
  {
     out = new ThingC();
     out->mergeFrom(inout, out); // (1)
  }
  catch (const EverythingHasGoneHorriblyWrongException& ex)
  {
     // (2) out and inout not guaranteed valid here..
  }
  return 0;
}

したがって、(1) で何か問題が発生し、ポイント (2) で「out」または「inout」が悪い状態になると、スコープ ガード sg2/sg3 は例外をスローします...そしてそれらの例外は、真の原因。

このシナリオで動作するパターン/規則はありますか? 明らかな何かが欠けていますか?

4

2 に答える 2

6

オブジェクト バリデータによって保護されたコード ブロックで例外が発生した場合、C++ ランタイムは を呼び出しますterminate。他の例外が処理されている間は、デストラクタのように例外をスローすることはできません。したがって、デストラクタから例外をスローしないでください (詳細はこちら)。例外をスローする代わりに、アサートを使用するか、エラーをログに記録する必要があります。

不変条件をチェックするよりも、不変条件が決して壊れないことを保証する方がよいでしょう。これは例外安全性と呼ばれます。基本的な例外安全性 (不変条件の保持) は、通常、ステートメントを巧妙に並べ替えてRAIIを使用するだけで簡単に達成できます。

例外安全技術の例:

class String {
  char *data;

  char *copyData(char const *data) {
    size_t length = strelen(rhs->data);
    char *copy = new char[length];
    memcpy(data, rhs->data, length);
    return data;
  }

public:
  ~String() { delete[] data; }

  // Never throws
  void swap(String &rhs) { std::swap(data, rhs->data); }

  // Constructor, Copy constructor, etc.
};

// No exception safety! Unusable!
String &String::operator = (String const &rhs) {
  if(&rhs == this) return *this;

  delete[] data;
  data = copyData(rhs->data); // May throw
}

// Weak exception safety
String &String::operator = (String const &rhs) {
  if(&rhs == this) return *this;

  delete[] data;
  data = 0; // Enforce valid state
  data = copyData(rhs->data); // May throw
}

// Strong safety 1 - Copy&Swap with explicit copy
String &String::operator = (String const &rhs) {
  String copy(rhs);// This may throw
  swap(copy);// Non-throwing member swap
  return *this;
}

// Strong safety 2 - Copy&Swap with pass by value
String &String::operator = (String rhs) {
  swap(rhs);// Non-throwing member swap
  return *this;
}
于 2012-05-22T20:41:17.317 に答える
2

スコープガードにアサーションを入れるのは興味深い。これは通常の使用例ではありませんが、カバレッジを改善することは悪い考えではありません。

すでに例外を処理している場合は、別の例外をスローできないことに注意してください。inout、またはの問題はinout他の場所に委任できないため、すぐに対処する必要があります。

アサーションに違反したときにデバッグ メッセージを出力することだけが必要な場合 ( の予想される動作Assert)、メッセージを出力してそのまま続行してください。例外をまったく混乱させないでください。

Assertより大きな例外処理メカニズムに結びつける必要がある場合、例外オブジェクトはAssert実際に生成されるものに対応する構造を持つ必要があります。しかし、その状態を適切な例外オブジェクトに入れるのは簡単ではありません。Assertスタックの巻き戻し中、例外が処理される前、rethrow ie ( try { throw; } catch ( structured_e & ) {}) によってアクセス可能になる前に呼び出されます。によって初期化された現在の構造化例外を格納するには、スレッドローカル変数が必要ですstructured_e::structured_e()

簡単に言えば、私のアドバイスはWeakAssert、例外をスローしないデストラクタとスコープ ガードで使用するために別のものを提供することです。

例外とデストラクタを組み合わせるときに賢くない理由については、Herb Sutter の記事も参照してください。

于 2012-05-23T07:06:42.903 に答える