2

我将在一个巨大的项目中使用库,其中异常用于错误处理。该库使用返回码进行错误处理,这意味着所有函数都将返回一个常量,定义函数是否成功或发生任何错误。

如果库函数失败,我的函数经常不得不中止。有什么比下面代码片段中给出的示例实现更好的方法来处理这些错误?

我的问题的一个主要问题是在每次评估返回代码后释放先前分配的内存。这是容易出错且麻烦的。我希望找到一些指导方针来防止每次调用库函数时释放...

void examplefunc()
{
  T* pT = new T();
  U* pU = new U();
  Q myQ;
  int iRes = CONST_SUCCESS;

  if ((iRes = myQ.func1())!= CONST_SUCCESS)
  {
    delete pU;
    delete pT;
    throw translateException(iRes);  // providing exc      
  }
  if ((iRes = mQ.func2())!= CONST_SUCCESS)
  {
    delete pU;
    delete pT;
    throw translateException(iRes);  // providing exc 
  }

  delete pU;
  delete pT;
  return;
}

感谢您的所有建议。有什么我可以遵循的指导方针吗?

最好的问候山姆

4

6 回答 6

4

使用智能指针。例如,而不是:

T* pT = new T();

你可能会使用 C++11 的unique_ptr

std::unique_ptr<T> pT = make_unique<T>();

只要包装对象超出范围,unique_ptr 就会自动销毁内部指针。

于 2012-09-29T20:24:08.160 回答
1

您应该为第 3 方代码创建某种包装器。最佳做法是永远不要直接使用第 3 方代码,因为如果它发生更改,您将被迫执行许多更改。

于 2012-09-29T20:19:36.970 回答
1

为什么不创建智能指针(在 C++98 或pTC ++11 中),在检测到错误时抛出错误并让智能指针在销毁时完成它们的工作(这也将在异常时发生)。pUstd::auto_ptrstd::unique_ptr

于 2012-09-29T20:21:46.687 回答
1

作为参考(如果您以前没有看过它),这里是使用智能指针的 C++11 版本。它相当于您的代码,除了:

  • 在抛出pT的情况下它不会泄漏new U()
  • 同样,如果有其他东西抛出(Q等的构造函数) ,它也不会泄漏
  • 它在删除然后销毁myQ 之前销毁,而您的代码先删除这些然后销毁。pUpTmyQ

通常破坏的顺序无关紧要,但如果它确实重要,则需要注意这一点。如果您需要以相反的创建顺序以外的任何方式销毁您的对象,那么它们的生命周期不能简单地通过它们的范围来控制。

void examplefunc()
{
    std::unique_ptr<T> pT(new T());
    std::unique_ptr<U> pU(new U());
    Q myQ;
    int iRes;

    if ((iRes = myQ.func1())!= CONST_SUCCESS)
    {
        throw translateException(iRes);  // providing exc
    }
    if ((iRes = mQ.func2())!= CONST_SUCCESS)
    {
        throw translateException(iRes);  // providing exc 
    }
    return;
}

我当然假设TandU对象不能简单地放在堆栈上——你没有说明为什么不这样做,但你问的事实表明这是有原因的!

在新旧代码之间混合原始指针和智能指针时,这段代码根本不会引起任何问题——这个函数以前负责释放它分配的内容,现在仍然如此。如果您需要将指向TorU对象的指针传递给一些采用原始指针的预先存在的代码:

  • 没有所有权,然后通过pT.get()
  • 拥有所有权(如果此函数并不总是负责删除,因为有时它会将指针传递或返回给具有该责任的其他人),然后传递pT.release().

然后,您可以根据需要在更多现有代码中使用智能指针。

最后,可以用 C++03 编写上面的代码std::auto_ptr,但是你必须非常小心auto_ptr,它很容易出错。unique_ptr是“更安全”但不能在 C++03 中完全实现。boost::scoped_ptr如果您不需要release().

于 2012-09-30T00:33:56.843 回答
0

在堆栈上而不是在堆上构造 T 和 U。然后它们会自动解构。只要有可能,您就应该这样做。

于 2012-10-01T10:38:08.280 回答
-1

如果您不想一遍又一遍地重复相同的内容,请编写一个执行以下操作的函数:

void throwErrorAndCleanup(T* pT, U* pU, int iRes)
{
    delete pU;
    delete pT;
    throw translateException(iRes);
}

另一种方法是使用 goto。使用 goto 是一个颇受争议的话题,但是对于像这样的错误清理,它通常是有意义的(特别是你可能会发现随着函数的进行需要清理更多的东西,所以你可以创建更多的跳转目标来清理额外的东西和然后落入较早的清理中)。

void examplefunc()
{
  T* pT = new T();
  U* pU = new U();
  Q myQ;
  int iRes = CONST_SUCCESS;

  if ((iRes = myQ.func1())!= CONST_SUCCESS)
  {
    goto error;
  }
  if ((iRes = mQ.func2())!= CONST_SUCCESS)
  {
    goto error;
  }

  delete pU;
  delete pT;
  return;

  error:
  delete pU;
  delete pT;
  throw translateException(iRes);  // providing exc 
}
于 2012-09-29T20:17:55.117 回答