如何构造清理代码? 我们在一个编译器不支持异常的C++平台上开发应用程序。

如何构造清理代码? 我们在一个编译器不支持异常的C++平台上开发应用程序。,c++,c,C++,C,当我们调用API时,我们需要检查返回结果中的错误代码。有时,当错误发生时,我们需要进行一些清理。我们定义一个宏CHECK\u ERROR\u GOTO来检查错误代码。所以代码是这样的 int res = callAPI1(); CHECK_ERROR_GOTO(res, cleanup); A* object1 = callAPI2(); // HERE WE WILL GET ERROR, //BECAUSE GOTO SKIP

当我们调用API时,我们需要检查返回结果中的错误代码。有时,当错误发生时,我们需要进行一些清理。我们定义一个宏CHECK\u ERROR\u GOTO来检查错误代码。所以代码是这样的

int res = callAPI1();
CHECK_ERROR_GOTO(res, cleanup);
A* object1 = callAPI2();    // HERE WE WILL GET ERROR, 
                             //BECAUSE GOTO SKIP INITIALIZATION OF OBJECT1
CHECK_ERROR_GOTO(object1 == NULL, cleanup);
C* object2 = callAPI3(object2);
CHECK_ERROR_GOTO(object2 == NULL, cleanup)
return 0;
cleanup:
    //Cleanup code here.
    delete object1
    delete object2
正如代码所示,我们将通过goto初始化object1,因此我们需要将
object1
object2
放在函数头中,这是不好的,因为我们应该将其放在需要使用的位置。和添加{}来创建局部作用域不起作用,因为清理代码需要使用局部变量


不管怎样,我们可以安排代码,这样我们就不需要在函数的开头放变量initialize了?

我喜欢这样做:

int foo()
{
  A* object1 = 0; // make sure you initialize those two
  C* object2 = 0; // so that further delete calls always work

  for (;;)
  {
    int res = callAPI1();
    if (res == -1)
      break;

    object1 = callAPI2();
    if (object1 == 0)
      break;

    object2 = callAPI3(object1);
    if (object2 == 0)
      break;

    return 0;
  }

  delete object1;
  delete object2;

  return -1;
}
然而,并非总是可以像上面那样清晰地表达代码。但当它是,我觉得它很好。它的优点是将清理代码放在一个地方(就像一个
goto
)。否则它可能无法维护,如:

// don't do this:
if(object2==0)
{
  delete object1;
  return 0;
}
// ... more code ...
if(object3==0)
{
  delete object2;
  delete object1;
  return 0;
}
// ... more code ...
if(object4==0)
{
  delete object3
  delete object2;
  delete object1;
  return 0;
}

没错,这不是最美妙的代码,但它可以:

int res = callAPI1();
if (!res) /* inverse of CHECK_ERROR_GOTO(res, cleanup); ??? */
{
    A * object1 = callAPI2();

    if (object1 != null) /* inverse of CHECK_ERROR_GOTO(object1 == NULL, cleanup); */
    {
        C * object2 = callAPI3(object1); //I suppose you want object1 here and not 2

        if (object2 != null) /* inverse of CHECK_ERROR_GOTO(object2 == NULL, cleanup);*/
        {
            /* if we reached here then both object1 and 2 are created
               thus delete both before returning to not get any memory leaks */
            delete object1;
            delete object2;

            return 0;
        }

        /* if we reached here then the creation of object2 has failed.
           Only delete object1 because object2 is not initialized. */
        delete object1;

        /* do some kind of return here? like -3 */
    }


    /* do some kind of return here? like -2 */
}

/* do some kind of return here? like -1*/

你说你的编译器不支持异常。但是它支持在早期返回时执行析构函数吗?我当然希望是这样。你应该通过一个简单的测试来确定

这意味着您可以使用智能指针和其他类来释放析构函数中的资源

int res = callAPI1();
if (!res) return -1;
scoped_ptr<A> object1(callAPI2());
if (!object1) return -1;
scoped_ptr<C> object2(callAPI3(object1.get()));
if ((!object2) return -1;
return 0;

注意:
return
语句应该在清理部分之后(除非您以某种方式返回这些对象…事实上,它现在看起来像一个mem泄漏)我真羡慕您必须处理这个绝对讨厌的问题。代码缺少一些分号。为什么不希望在函数开始时初始化它们?似乎是一个奇怪的要求?将所有变量放在函数的开头并不是那么糟糕。C语言最初是这样的,你必须这样做,它是当时的标准。嗯,OP explicit希望它没有A和B的首字母。我知道这是主观的,但这个
for+break
在我看来很奇怪。至少
goto cleanup
传达了一个清晰的信息,并且可以轻松地与更复杂的代码一起使用。谢谢,这解决了我的大多数问题,我甚至认为这是不可能完成的任务:)。我将此标记为答案,如果清理代码复杂,即使此解决方案也不总是可行的。如果您有复杂的清理代码,您可能需要编写自定义清理类,但始终可以使用RAII。下面是一篇很好的文章,展示了这一点:
template <typename T>
class scoped_ptr {
  T* raw;

  struct bool_dummy { void fn() {} };
  typedef void (bool_dummy::*pseudo_bool)();

  scoped_ptr(const scoped_ptr&); // non-copyable
  scoped_ptr& operator =(const scoped_ptr&);

public:
  scoped_ptr(T* raw) : raw(raw) {}
  ~scoped_ptr() { delete raw; }

  T* get() const { return raw; }
  T& operator *() const { return *get(); }
  T* operator ->() const { return get(); }
  operator pseudo_bool() const { return get() ? &bool_dummy::fn : 0; }
  bool operator !() const { return !get(); }
};