25

希望我有一个简单的问题 - 当异常发生时,如何在 try 块中分配一个空闲内存?考虑以下代码:

try
 {
  char *heap = new char [50];
        //let exception occur here
  delete[] heap;
 }
 catch (...)
 {
  cout << "Error, leaving function now";
  //delete[] heap; doesn't work of course, heap is unknown to compiler
  return 1;
 }

如何在分配堆后释放内存并且在调用之前发生异常delete[] heap?是否有规则不在这些 try .. catch 块中分配堆内存?

4

9 回答 9

34

学习RAII 成语Resource Acquisition Is Initialization)!参见例如关于 RAII 的 Wikipedia 文章

RAII 只是一般的想法。它用于例如 C++ 标准库std::unique_ptrstd::shared_ptr模板类中。


RAII 成语的非常简短的解释:

基本上,它是try..finally在其他一些语言中发现的块的 C++ 版本。RAII 成语可以说更灵活。

它是这样工作的:

  • 您围绕您的资源(例如内存)编写一个包装类。析构函数负责释放资源。

  • 作为本地(自动)变量,您可以在范围内创建包装类的实例。一旦程序执行离开该范围,将调用对象的析构函数,从而释放资源(例如内存)。

重要的一点是,如何退出范围并不重要。即使抛出异常,仍然退出作用域并且仍然调用包装对象的析构函数。


非常粗略的例子:

// BEWARE: this is NOT a good implementation at all, but is supposed to
// give you a general idea of how RAII is supposed to work:
template <typename T>
class wrapper_around
{
  public:
    wrapper_around(T value)
        : _value(value)
    { }
    T operator *()
    {
        return _value;
    }
    virtual ~wrapper_around()
    {
        delete _value;  // <-- NOTE: this is incorrect in this particular case;
                        // if T is an array type, delete[] ought to be used
    }
  private:
    T _value;
};
// ...

{
    wrapper_around<char*> heap( new char[50] );
    // ... do something ...

    // no matter how the { } scope in which heap is defined is exited,
    // if heap has a destructor, it will get called when the scope is left.
    // Therefore, delegate the responsibility of managing your allocated
    // memory to the `wrapper_around` template class.
    // there are already existing implementations that are much better
    // than the above, e.g. `std::unique_ptr` and `std::shared_ptr`!
}
于 2010-06-15T19:32:58.423 回答
9

好的Java程序员先生:

try
{
    // Exception safe dynamic allocation of a block of memory.
    std::vector<char>  heap(50);

    // DO STUFF

    // Note in C++ we use stack based objects and their constructor/destructor
    // TO give a deterministic cleanup, even in the presence of exceptions.
    //
    // Look up RAII (bad name for a fantastic concept).
}
catch (...)
{
    cout << "Error, leaving function now";
    return 1;  // Though why you want to return when you have not fixed the exception is
               // slightly strange. Did you want to rethrow?
}
于 2010-06-15T19:46:18.613 回答
9

一般的答案是使用 RAII。

但是,可以通过将变量移出 try{} 范围来解决它:

char * heap = NULL;
try {
  heap = new char [50];
  ... stuff ...
} catch (...) {
  if (heap) {
    delete[] heap;
    heap = NULL;
  }
  ... however you want to handle the exception: rethrow, return, etc ...
}

请注意,我不建议将其作为一种好的做法 - 但只有在您真正了解风险并且仍然愿意承担风险时才使用这种方法。就个人而言,我会使用 RAII。

和平

于 2010-06-15T20:38:37.933 回答
6

要么移动new之前try,使指针仍在范围内,要么使用智能指针,如shared_ptror unique_ptr(在紧要关头,auto_ptr,但它有问题),它将在退出时为您清理。异常是智能指针重要的一个重要原因。

于 2010-06-15T19:34:36.250 回答
3

如上所述,“正确”答案是 RAII 和 shared_ptr ,但只是为了完整:在您的示例中,您可以替换

char *heap = new char [50];

char *stack = static_cast<char*>( alloca(50) );

alloca几乎与 相同malloc,除了它在堆栈而不是堆上分配内存,因此无论您如何退出(抛出或现在),内存都将被回收,并且不需要删除或释放。

于 2010-06-15T19:55:27.430 回答
1

我必须同意所有那些说 RAII 的人,但是,我会使用 Boost 的shared_array而不是 auto_ptr。自动指针调用delete而不是“删除 []”,这将导致数组泄漏。

于 2010-06-15T19:56:49.927 回答
0

最简单的方法是在 try 块之前声明变量,然后在块内进行初始化。

于 2010-06-15T19:35:51.683 回答
0

是的-如果您正在考虑简单性-try 块外部的指针是解决方案。

问候

于 2010-06-23T14:24:37.533 回答
-4

同意关于 RAII 和智能指针的答案。

但是,如果你坚持,你可以这样做:

try { dangerous operations } 
catch { cleanup; throw; }
于 2010-06-15T19:37:31.777 回答