9

我在很多地方都使用过if...else语句,但是我对异常处理不熟悉。这两者的主要区别是什么?

例如:

 int *ptr = new (nothrow) int[1000];
 
 if (ptr == NULL) {
     // Handle error cases here...
 }

或者

  try
  {
    int* myarray= new int[1000];
  }
  catch (exception& e)
  {
    cout << "Standard exception: " << e.what() << endl; 
  }

所以我们在这里使用标准类来处理异常,它有一些构建函数,比如e.what(). 所以可能是优势。除此之外,我们还可以使用所有其他功能处理if...else。使用异常处理还有其他优点吗?

4

5 回答 5

3

要收集答案中的评论内容:

自 1998 年标准化以来,new失败时不返回空指针而是抛出异常,即std::bad_alloc. 这与 C 不同,malloc也可能与 C++ 的一些早期预标准实现不同,其中new 也可能返回 NULL(我不知道,tbh)。

在 C++ 中,有可能在分配失败时获取空指针而不是异常:

int *ptr = new(std::nothrow) int[1000];

所以简而言之,您拥有的第一个代码不会按预期工作,因为它是在存在 C++ 异常的情况下尝试 C 风格的错误处理。如果分配失败,将抛出异常,永远不会进入 if 块并且程序可能会终止,因为您没有捕获bad_alloc.

有很多文章将一般错误处理与异常与返回码进行了比较,如果试图在这里涵盖该主题将会走得更远。例外的原因包括

  • 函数返回类型不被错误处理占用,但可以返回实际值——不需要“输出”函数参数。
  • 您不需要在每个函数中处理每个函数调用的返回,但可以在调用堆栈的某些级别捕获异常,您实际上可以在其中处理错误
  • errno与一个全局变量和一个返回的错误代码相比,异常可以将任意信息传递给错误处理站点。
于 2013-08-19T06:50:17.017 回答
2

主要区别在于使用异常处理的版本至少可以工作,而使用if语句的版本可能无法工作。

你的第一个片段:

int *ptr = new int[1000];

 if (ptr == NULL) {
     // Handle error cases here...
 }

...似乎假设new在失败的情况下将返回一个空指针。虽然这曾经是真的,但已经很久没有了。对于任何当前合理的编译器,new只有两种可能性:成功或抛出。因此,您的第二个版本与 C++ 的工作方式一致。

如果你真的想使用这种风格,你可以重写代码让它在失败的情况下返回一个空指针:

int *ptr = new(nothrow) int[1000];

if (ptr == NULL) {
     // Handle error cases here...
}

在大多数情况下,new无论如何你都不应该直接使用——你应该真正使用std::vector<int> p(1000);并完成它。

有了这个,我觉得有必要为大量代码添加这一点,这可能是最有意义的,不做任何事情并简单地假设内存分配会成功。

曾经(MS-DOS)如果您尝试分配比可用内存更多的内存,内存分配实际上会失败是相当普遍的——但那是很久以前的事了。如今,事情并不那么简单(通常)。当前系统使用虚拟内存,这使情况变得更加复杂。

在 Linux 上,通常会发生的情况是即使内存并不真正可用,Linux 也会执行所谓的“过度提交”。你仍然会得到一个非空指针,就好像分配成功了一样——但是当你尝试使用内存时,就会发生不好的事情。具体来说,Linux 有一个所谓的“OOM Killer”,它基本上假设内存不足是错误的迹象,所以如果发生这种情况,它会尝试找到有错误的程序,并杀死它/它们。对于大多数实际目的,这意味着您的程序可能会被杀死,而其他(半任意选择的)程序也可能会被杀死。

Windows 更接近 C++ 所期望的模型,因此如果(例如)您的代码在无人值守的服务器上运行,则分配实际上可能会失败。然而,早在它失败之前,它就会把机器的其余部分拖到膝盖上,疯狂地交换,试图使分配成功,但注定失败。如果用户当时实际上正在操作机器,他们通常会杀死您的程序或杀死其他一些程序,以便为您的代码释放足够的内存以相当快地获得请求的内存。

在这些情况下,针对分配可能失败的假设进行编程并不是特别现实。对于大多数实际目的,会发生以下两种情况之一:分配成功或程序终止。

这又回到了之前的建议:在典型情况下,您通常应该只使用std::vector,并假设您的分配成功。如果您需要提供除此之外的可用性,则只需以其他方式执行此操作(例如,如果进程死亡,则重新启动进程,最好以使用较少内存的方式)。

于 2013-08-19T07:07:01.840 回答
2

如前所述,您最初的 if-else 示例仍会从 C++98 开始抛出异常,尽管添加nothrow(如编辑)应该使其按需要工作(返回null,从而触发 if 语句)。

下面我将假设,为简单起见,如果 if-else 处理异常,我们有函数在异常时返回 false。

if-else 上的异常的一些优点,我想不到:

  • 您知道日志记录/调试/错误修复的异常类型

    例子:

    当函数抛出异常时,您可以在合理的范围内判断代码是否存在问题,或者您无法处理的问题,例如内存不足异常。

    使用 if-else,当函数返回 false 时,您不知道该函数中发生了什么。

    当然,您可以使用单独的日志记录来记录此信息,但为什么不直接返回包含异常详细信息的异常呢?

  • 您不需要有一堆 if-else 条件来将异常传播到调用函数

    示例:(包含用于指示行为的评论)

    bool someFunction() // may return false on exception
    {
       if (someFunction2()) // may return false on exception
          return false;
    
       if (someFunction3()) // may return false on exception
          return false;
    
       return someFunction4(); // may return false on exception
    }
    

    (有很多人不喜欢有多个返回语句的函数。在这种情况下,你会得到一个更混乱的函数。)

    相对于:

    void someFunction() // may throw exception
    {
       someFunction2(); // may throw exception
       someFunction3(); // may throw exception
       someFunction4(); // may throw exception
    }
    

if-else 的替代或扩展是错误代码。为此,将保留第二点。有关该异常与异常之间的比较的更多信息,请参见this 。

于 2013-08-19T07:09:00.367 回答
1

如果您在本地处理错误,if ... else则更清洁。如果发生错误的函数没有处理错误,则抛出异常以传递给调用链中更高的人。

于 2013-08-19T13:06:20.927 回答
0

首先,如果 new[] 运算符由于未处理的异常而引发异常,则带有 if 语句的第一个代码将终止程序。您可以在此处查看此类内容,例如:http ://www.cplusplus.com/reference/new/operator%20new%5B%5D/

在许多其他情况下也会引发异常,不仅是在分配失败时,而且它们的主要特征(在我看来)是将应用程序中的控制向上移动(到处理异常的地方)。我建议您阅读有关异常的更多信息,最好阅读 Scott Meyers 的“More Effective C++”,其中有关于异常的精彩章节。

于 2013-08-19T06:49:26.563 回答