c++和Java异常处理的区别

Difference between exception handling in C++ and Java?

本文关键字:区别 异常处理 Java c++      更新时间:2023-10-16

在Java中,如果特定的代码行导致程序崩溃,则捕获异常,程序继续执行。

然而,在c++中,如果我有一段导致程序崩溃的代码,比如:

try
{
    int x = 6;
    int *p = NULL;
    p = reinterpret_cast<int*>(x);
    *p = 10; // the program crashed here
    cout << "x = " << *p << endl;
}
catch(const char* Message)
{
    cout << "There is an run-time error";
}

程序仍然崩溃,异常没有被捕获。

那么c++中异常处理的意义是什么呢?我误解什么了吗?

导致崩溃的行是对无效指针的解引用。在c++中,这不会抛出异常。相反,它是未定义的行为。

c++中没有空指针异常,不像Java会抛出空指针异常。相反,解引用无效指针将导致未定义的行为。未定义的行为并不总是意味着崩溃,但是如果它崩溃了,你是幸运的。

语言概述:

Finally and RAII

c++和Java之间最显著的区别之一是Java支持finally语句。不管前面的catch块中的代码是否执行,finally块中的代码总是运行。例如:

try
{
}
catch (SomeException e)
{
}
finally
{
  //code here is always exectued.
}

finally语句的目的是允许程序员在该点进行清理,即释放套接字,关闭文件句柄等…即使Java运行垃圾收集器,垃圾收集也只应用于内存而不应用于其他资源。仍然有需要手动处理资源的情况。现在c++没有finally语句,所以该语言的用户被建议坚持RAII原则(资源获取是初始化)Stroustrup对此有一个解释:http://www.stroustrup.com/bs_faq2.html#finally。我更喜欢叫它Resource destruction is deallocation,但基本上当你的对象超出作用域时,调用析构函数,那么析构函数应该释放对象维护的任何资源。

例如,c++ 11x提供了一个std::unique_ptr来管理:

void foo()
{
  std::unique_ptr<T> t(new T)
  try
  {
    //code that uses t
  }
  catch (...)
  {
  }
}

通过new分配的资源将在函数结束时被删除。

捕获所有语句

因为Java中的所有异常都继承自一个公共基类Exception,如果你想让catch子句捕获任何异常,那么就像这样设置它:

catch (Exception e)
{
  //any exception thrown will land here.
}

在c++中,对可以抛出什么没有限制,也没有所有异常的公共基类。标准做法是通过继承std::exception来形成自定义异常类,但语言不强制这样做。相反,有一种特殊的语法用于捕获所有异常:

catch (...)
{
}

未处理的异常

这是两种语言表现不同的另一个领域。在c++中,未被捕获的抛出异常将调用std::terminate。std::terminate的默认行为是调用abort,它生成一个SIGABRT,然后整个程序停止。

在Java中,该行为是打印堆栈跟踪并终止发生未捕获异常的线程。然而,由于Java程序员可能会提供UncaughtException处理程序,因此其行为可能与终止线程的默认行为完全不同。

不是所有的崩溃都是由于未处理的异常。对于您的示例,c++标准规定对NULL指针解引用会导致未定义行为。在Windows中,您可以使用结构化异常处理(SEH)来处理导致程序崩溃的问题,而不会抛出c++异常:__try/__except/__finally。在Unix中,可以设置特殊的信号处理程序。


还有,你的代码中有一个错误。const char *的异常处理程序只会在抛出这种类型的异常时调用。对于标准异常,您应该捕获std::exception或它的适当子类。要捕获任何c++异常,请使用catch (...) .

实际上,您可以在c++中捕获系统异常。有一个编译器选项(至少在Visual Studio中)可以让您捕获访问冲突异常(这就是为什么您的程序崩溃)。

Java更加谨慎,因此产生了复杂的错觉。

思考以下内容:

Java:

int x[10];
int i = 20;
try
{
    int k = x[i];
}
catch (ArrayIndexOutOfBoundsException ex)
{
     //will enter here
}

Int c++:

int x[10];
int i = 20;
try
{
    if ( i >= 10 )
        throw new "Index out of bounds";
    int k = x[i];
}
catch (...)
{
    //now it will enter here
}

这一切都与您是否希望将更多的事情留给运行时环境(如Java的情况)或您自己想要处理事情有关。c++给了你更多的控制,但是你必须更加注意。

如果不处理异常,你的Java程序也会崩溃——想想看,如果一个方法显式地抛出一个异常,你不能不处理它,因为编译器不允许你这样做。如果不是显式的,你的程序仍然会崩溃,除非被try/catch包围。

如果你问为什么系统异常不能在c++中处理,我已经回答了:它们可以,只是在默认情况下,这是关闭的。

在任何语言中,异常的意义都是或者应该是要处理的异常情况。但仍有一些情况你可以做出合理的解释关于全局程序状态的假设,以及可能的恢复。编程错误通常意味着您不能对全局程序状态,并且必须尽快终止代码可能的,执行最少的额外代码(因为你不知道什么))。

在Java中,几乎所有的事情都是通过异常来报告的。从常见的,预期的错误("未找到文件")打开文件)导致严重的内部错误(java.lang.VirtualMachineError)。c++给了你选择:如果你检测到编码错误(断言失败),您可以中止该进程立即(通常比在一个未知的地方瞎混更合适)状态);如果"错误"是通常会发生在日常操作("文件未找到"),可以测试状态,或者使用返回代码(同样,通常比异常更合适)。对于一个这两种情况之间的数量(例如内存不足),c++使用例外。

当然,在给定的应用程序中,什么是最合适的是不同的:当然,在某些情况下,"未找到文件"是例外情况(例如,如果该文件是应用程序的一部分,没有它就不能运行),并且需要一个例外。类似地,在特定情况下,空指针可以用于控制程序逻辑(if ( ptr == NULL ) ...)或对应一个例外情况(if ( ptr == NULL ) throw ...);在在其他情况下,空指针是编程错误( assert( ptr != NULL))。
    c++和Java都有try和catch块,但是Java有一个新的块,总是在try和catch之后执行。Java只抛出对象,但c++抛出数据(原语和指针&对象)。
  1. c++有catch(…){}捕获所有类型的异常,Java有catch(exception e){}捕获所有类型的异常。