堆栈展开

时间:2014-04-11 11:47:10

标签: c++ exception-handling

我被告知将所有指针放在catch块中都是错误的OO编程。清除发生在catch块中。它如何违反OO设计的每个规则?

以下是示例代码:

#include <iostream>
#include <string>
using namespace std;

class Error
{
  friend int main();
  public:
    Error(int* p, string m) : arr(p), msg(m) { }
  private:
    int*   arr;
    string msg;
};

void initialize();


int main()
{
  try {
    initialize();
  }
  catch(Error& err) {
    cout<<endl<< "Error! "<< err.msg <<endl<<endl;
    delete [] err.arr;
  }
  return 0;
}

void initialize()
{
  int size;

  cout<<"Enter the number of elements: ";
  cin >> size;

  int* myArray = new int[size];

  cout<<"Enter the elements: " <<endl;
  for (int i=0; i<size; ++i)
    cin >> myArray[i];

  if (!cin.good())
    throw Error(myArray, (string)"bad input!");

  cout<<endl<<"You entered:"<<endl;

  for (int i=0; i<size; ++i)
    cout << myArray[i] << " ";
  cout<<endl;

  delete [] myArray;

}

请忽略此行。我只是试着发布这个问题。

3 个答案:

答案 0 :(得分:2)

问题是,你必须以所有可能的方式删除数组以离开该函数。如果您只有一种或两种方式,这可能很容易,但更容易混淆。即使在您的代码中,您已经在函数外部进行了一次删除,但却很难找到它。

使用智能指针来定位该问题。当他们超出范围时,他们会释放内容。这样你就不必为数组的破坏而烦恼了。一旦完成该功能,阵列将被销毁。

以下是智能指针的一些文档: unique_ptr shared_ptr

答案 1 :(得分:2)

为了处理资源,C过去专注于管理执行路径。程序员必须确保为每条可能的路径释放资源。

所以以这样的代码结束是正常的: 请注意,大多数代码都用于处理错误。

HRESULT 
CreateNotifyIcon(NotifyIcon** ppResult) 
{ 
    NotifyIcon*      icon = 0; 
    Icon*            inner = 0; 
    const wchar_t *  tmp1 = 0; 
    HRESULT          hr = S_OK; 

    if ( SUCCEEDED(hr) ) { 
        icon = new (nothrow) NotifyIcon(); 
        if ( !icon ) hr = E_OUTOFMEM; 
    } 

    if ( SUCCEEDED(hr) ) 
        hr = icon->set_text("Blah blah blah"); 

    if ( SUCCEEDED(hr) ) { 
        inner = new (nothrow) Icon(...); 
        if ( !inner ) 
            hr = E_OUTOFMEM; 
        else { 
            Info info; 
            hr = GetInfo( &info ); 

            if ( SUCCEEDED(hr) ) 
                hr = icon->set_icon(inner, info); 
            if ( SUCCEEDED(hr) ) 
                inner = NULL; 
        } 
    } 
    if ( SUCCEEDED(hr) ) 
        hr = icon->set_visible(true); 

    if ( SUCCEEDED(hr) ) { 
        *ppResult = icon; 
        icon = NULL; 
    } else { 
        *ppResult = NULL; 
    } 

    cleanup: 
    if ( inner ) delete inner; 
    if ( icon ) delete icon; 
    return hr; 
}

在C ++中,这不是正确的方法,因为你有例外。例如:

  String EvaluateSalaryAndReturnName( Employee e )
  {
    if( e.Title() == "CEO" || e.Salary() > 100000 )
    {
      cout << e.First() << " " << e.Last()
           << " is overpaid" << endl;
    }
    return e.First() + " " + e.Last();
  }

该代码段中有 23 different execution个路径。

所以C ++选择专注于资源。每个函数(应该)处理有限数量的资源。粗略地说,您在每个资源上放置一个监视程序,以确保它们被正确释放/释放。这只看门狗是 RAII 。实际上,无论执行路径是什么,您都可以100%确定将调用堆栈上分配的所有对象的析构函数。这样,通过将您的资源放入RAII对象(STL容器,std :: unique_ptr,...),您可以处理异常,而不会出现任何泄漏资源问题。

看看差异: BAD WAY

void function(int n){
  int* p = 0;
  int* c = 0;

  try{
    p = new int[n];
    c = new int[n*2];
  }
  catch(std::exception const& e){
    delete[] c;
    delete[] p;
    throw;
  }

  delete[] c;
  delete[] p;
}

int main(){
  try{
    function(1000);
  } catch (std::exception const& e){
    std::cerr<<e.what()<<std::endl;
  }
}

好的方式

void function(int n){
  std::unique_ptr<int[]> p(new int[n]); //or std::vector
  std::unique_ptr<int[]> c(new int[n*2]);
}
int main(){
  try{
    function(1000);
  } catch (std::exception const& e){
    std::cerr<<e.what()<<std::endl;
  }
}

答案 2 :(得分:0)

C ++标准版n3337 § 15.2 / 3说:

  

为构造的自动对象调用析构函数的过程   在从try块到throw-expression的路径上称为“堆栈”   平仓。 (...)”

你的代码的问题是指向数组的指针是在try块中分配的,所以当控制到达catch块时它不再存在。你做不到

要纠正这个问题,你应该在try block:

之前声明一个指针
  int* myArray;

  try{
    function(1000); // allocate an array and point myArray to it
  } catch (std::exception const& e){
    delete [] myArray;  // OK, myArray pointer is valid here
  }

这将删除对象并将内存返回给系统。例如,在std::uninitialized_fill中采用这种方法。但是你有更好的可能性。为了摆脱释放内存的负担,您可以考虑将智能指针句柄用于数组(包装资源的类):将每个资源表示为class是名为RAII的方法的基础。

  try{
    MyArray myArray(1000); // allocate an array in constructor
  } catch (std::exception const& e){
    // destructor for myArray has deleted ints & returned memory already
  }