第一个免责声明:这不是为了引发“语言战争”。我的报告真的需要这个(关于这个主题的澄清),我只想有有效和扎实的论据。
好的,问题来了:
在 C++ 中,异常规范已从 C++11 标准中删除,因为它被认为弊大于利。
另一方面,在 Java 中,异常规范被认为是好的和有用的东西。
这两个概念(具有异常规范的目的)在这两种语言中是否不同,这就是为什么这两个社区对它们的看法不同,或者这些概念是相似/相同的?
哪一个?异常规范是好事还是坏事?或者它在 Java 中是好的,因为(在这里我想看看一些原因)而在 C++ 中是不好的,因为(原因在这里)。
感谢任何提供建设性帮助的人。
4 回答
除此之外,我认为 Java 和 C++ 异常规范之间的主要区别在于,在 Java 中,规范是函数类型的一部分,编译器强制您不能允许检查的异常转义函数,除非它们是一部分你的函数的类型。
因此,Java 检查异常提供了一种有限/有缺陷的方法来跟踪哪些函数抛出了哪些异常。C++ 异常规范提供了一种有限/有缺陷的方法来防止对函数的调用引发特定异常(或任何异常)。
由于它们有不同的目的,因此必须分别评估它们的成功或失败。就实际程序员避免使用它们而言,我认为您可以肯定地说它们都有些失败,但这就是您可以对两者进行相同评估的所有内容。这个程度对于 Java 来说是相当高的,而对于 C++ 来说是非常高的。它们所具有的有益用途以及每种语言的确切成功程度是不同的。
Java 和 C++ 中的异常规范以非常不同的方式工作。
从 C++ 的角度来看,这是一个非常好的信息来源。http://www.gotw.ca/publications/mill22.htm
C++ 设计的主要缺点是,如果您抛出意外异常,您的程序很可能会崩溃(有关详细信息,请参阅链接)。因此,异常规范是一个应用得太晚的约束。
Herb Sutter 关闭文章说:
Moral #1: Never write an exception specification. Moral #2: Except possibly an empty one, but if I were you I’d avoid even that.
Java 异常规范框架不同,您有两种类型的异常:已检查(编译时)和运行时异常(很像没有异常规范的 C++)。
对于已检查的异常,编译器将强制开发人员处理这些异常,否则应用程序将无法编译。这很好而且有用,但是运行时和检查异常之间的区别并不完全清楚,并且取决于开发人员,并且可能会产生混淆。即使使用简单的程序习语(例如检查异常规范和策略模式),检查异常也涉及困难的决定。
最终的结果,和往常一样,语言特征不清晰,是用的不好。例如,C# 设计者决定放弃它们。
我认为,对于这个特定主题,java 设计比 C++03 更好(我是 C++ 的忠实粉丝),因为它允许开发人员以更具描述性的方式编写更好的代码。但是,您需要花费精力(编码标准和代码审查)在您的开发团队中生成一致的代码。
几年前,Java 的已检查异常已经失宠,正如您所说的那样,它们造成的伤害多于帮助。现在的人们主要想办法避免它们,通常是用RuntimeException
s 包裹起来。
在 Effective Java 中,Josh Bloch 仍然在“异常但预期的结果”的情况下为已检查的异常辩护,但事实是,API 编写者并不是决定哪些结果是预期的,哪些不是的人。例如,即使是FileNotFoundException
在上下文中,也可能是意想不到的致命结果。
因此,至少,任何公共图书馆都不应该使用受检异常。
C++ 和 Java 在异常规范方面甚至没有可比性,因为它们在异常方面有所不同:
- C++ 仅在需要时使用动态分配,程序员尽可能使用堆栈分配(如果只是为了避免释放内存的问题)
- Java 几乎一直使用动态分配;对于用户定义类型的对象,对于数组(甚至是在编译时已知大小的数组)
在 C++ 中,许多函数可以保证永远不会抛出异常。我不是在谈论异常规范,而是关于函数的记录和实际行为。对于简单的函数调用的情况,异常规范是什么并不重要,只要没有可以抛出异常的语句即可。
[对于从不抛出异常的函数,当调用代码需要测量函数抛出异常的可能性时,异常规范确实很重要,以便以一种会使对象处于不良状态的方式调用非抛出函数如果中间有异常。这是noexcept
在现代 C++ 中完成的,在旧 C++ 中,这可以通过具有类型特征的更冗长的方式(同样,库之间不兼容)来完成。]
[请注意,编译器始终有权查看编译期间可见的函数体,以确定它是否可以抛出异常,并相应地优化调用代码(如果至少减少异常表的大小)。抛出规范仅在编译器看不到代码无法抛出时才对它有帮助。]
在 C++ 中,这个“从不抛出”要求可以被记录,并由编译器强制执行,带有一个空的抛出规范(无论是旧throw()
的还是现代的nothrow
)。
Java没有这种极其有用的保证。它可以声明某些函数不会抛出异常,这对于编写异常安全代码是没有用的。