使用范围保护作为代码合同

JBR*_*son 2 c++ design-by-contract code-contracts

因此,我们正在调查范围保护或类似机制的使用,以确保传入/传出对象的有效性和/或内部状态不变性,类似于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;
}
Run Code Online (Sandbox Code Playgroud)

因此,如果(1)中出现错误导致'out'或'inout'在第(2)点处于错误状态,则范围保护sg2/sg3将抛出异常......并且这些异常可能会掩盖真正的原因.

是否有任何模式/约定适用于此方案?我们缺少明显的东西吗?

Jur*_*aho 6

如果对象验证器保护的代码块中存在异常,则C++运行时将调用terminate.您不能像析构函数那样抛出异常,而正在处理其他异常.因此,您不应该从析构函数中抛出异常(详情请参见此处).您应该使用assert或记录错误,而不是抛出异常.

比检查不变量更好的是保证它们永远不会被破坏.这被称为异常安全.通过巧妙地重新排序语句和使用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;
}
Run Code Online (Sandbox Code Playgroud)