对异常处理的C ++编程实践的困惑

时间:2012-03-07 20:18:04

标签: c++ exception-handling

我有一个下面提到的C ++代码:

#include<iostream>
#include<stdexcept>

const long MAX = 10240000;

class Widget{
      public:
             Widget(){
                      ok = new int [1024];
                      prob = new int [100*MAX];
             }
             ~Widget(){
                       std::cout<<"\nDtoR of Widget is called\n";
                       delete ok; ok = NULL;
                       delete prob; prob = NULL;
             }
             //keeping below public: Intentionally
              int* ok;
              int* prob;
};


void func(){
    Widget* pw = NULL;  // <<<<--------------- Issue in Here
    try{
        pw = new Widget;
    }
    catch(...){
               delete pw;
               std::cout<<"\nIn catch BLOCK\n";
               if(pw->ok == NULL){
                      std::cout<<"\n OK is NULL\n";
               }
               throw;
    }
    delete pw;
}

int main(){
    try{
          func();
    }
    catch(std::bad_alloc&){
                     std::cout<<"\nError allocating memory."<<std::endl;
    }

    std::cout<<std::endl;
    system("pause");
    return 0;
}

现在在函数func()中,我看到两种不同的行为,具体取决于我是否设置指针&#39; pw&#39;为NULL,如果我设置&#39; pw&#39;指向NULL的指针(如上面的代码)。我的印象是它很好'&#39;练习首先将指针设置为NULL然后初始化它。但是当我将它初始化为NULL时,输出只显示&#34;在catch BLOCK&#34;后来的应用程序崩溃。但是如果我没有将pw指针设置为NULL,那么我会看到pw的析构函数被调用,并且显示以下输出没有任何应用程序崩溃。

  

Widget的DtoR称为

     

抓住BLOCK

     

确定是NULL

     

分配内存时出错。

     

按任意键继续。 。

我的问题是,为什么在我们没有初始化&#39; pw&#39;指向NULL的指针,在其他情况下,我们将其设置为NULL。为什么在一个案例中调用析构函数,为什么不在另一个案例中调用析构函数。

注意:此代码的目的是抛出bad_alloc exeption。

6 个答案:

答案 0 :(得分:6)

如果您未将pw设置为NULL,那么您将保持未初始化状态。然后,当“try”块中的new运算符抛出异常时,它永远不会返回,并且您进入catch块。由于new从未返回,pw仍未初始化,您将随机地址传递给delete。这会给你一个未定义的行为。

答案 1 :(得分:4)

在你的catch区块中,你有:

if(pw->ok == NULL)

此时,pwNULL(如果您没有初始化,则为垃圾)。 pw-ok尝试取消引用它,给出未定义的行为(在这种情况下崩溃)。

如果你没有初始化它,那么delete pw将在打印“catch”消息之前崩溃;最有可能的是,它会在崩溃之前打印出“Dtor”消息,但由于你坚定地处于未定义的行为领域,因此无法保证。

如果你做了初始化,那么delete pw是不必要但无害的;删除空指针定义为什么都不做。所以在这种情况下,在取消引用它之前,你不会崩溃。

无论如何,你有一个无法修复的内存泄漏 - 第一个分配ok = new int[1024]将成功,但你已经失去了唯一的指针。这就是为什么你应该总是使用智能指针,容器和其他RAII技术管理动态内存。

答案 2 :(得分:1)

你打算使用bad_alloc exeption.but你有更多未处理的异常! 首先删除pw然后使用它的指针是不行的!

           delete pw;
           if(pw->ok == NULL)

答案 3 :(得分:0)

由于行

,将pw设置为NULL时,您看到应用程序崩溃
if (pw->ok == NULL)

您正在取消引用导致崩溃的NULL。在另一种情况下,您正在删除垃圾,这将给您未定义的行为。

此外,在调用delete之后不应使用指针。这可能会导致各种奇怪的行为。

详细说明正在发生的事情。你的Widget构造函数抛出了分配异常。在这种情况下,ok的分配很可能已完成,但prob的分配失败。您的构造函数永远不会完成,泄漏分配给ok变量的内存。如果要确保清理内存,则应在构造函数中添加try catch。

Widget() : ok(NULL), prob(NULL)
{
    try
    {
        ok = new int [1024];
        prob = new int [100*MAX];
    }
    catch(...)
    {
        std::cout << "\nCtor of Widget threw exception\n";
        delete [] ok;
        delete [] prob;
        throw;
    }
}

答案 4 :(得分:0)

  1. 删除pw->ok后,为什么要拨打pw?它已经消失了。
  2. 你的构造函数应该有成员初始化
  3. 
    Widget():ok(NULL), prob(NULL) {
    ...
    }
    

    因为如果Widget()失败,你不知道哪个成员变量被初始化,哪个不能导致你的析构函数出现问题。

    1. 由于您使用int[]进行了分配,因此您需要在析构函数中delete[]而不仅仅是delete

答案 5 :(得分:-1)

最好将pw初始化为NULL,但删除它时,首先应检查pw是否为空。类似的东西:

if (pw) delete pw;

此外,如果pwNULL,则无法引用其成员。