有什么例外?

时间:2010-11-03 09:19:58

标签: c# c++ language-agnostic exception exception-handling

我们每天都在谈论异常处理。我们都知道,当执行遇到某些意外情况时,它就会被创建。

这里有几个问题:

  • 什么是例外?什么是它最低级别的内存组合?在.NET中,我可以将其视为某些异常 type 的对象实例。在本土世界,它是由什么组成的?一些数据是否存在?

  • 如果程序员没有明确抛出异常,谁会创建异常,如下面的代码所示?它是某些语言运行库提供的支持的一部分吗?

    SomeException e = new SomeException(); 扔e;

  • 工作范例有什么例外?是否真的发生了一些错误,语言运行时会创建相应数据结构/类型的实例来表示错误的详细信息?

  • 我们如何知道运行时所有可能的意外情况,从而创建足够的异常数据结构/类型来表示它们?

感谢您的回复。

7 个答案:

答案 0 :(得分:14)

您的问题的答案很大程度上取决于我们所谈论的语言。

C根本没有例外 ,但有专有语言扩展。
C ++ 有一种方法可以在代码中的任何一点“抛出” 任意对象 并“抓住”它调用堆栈上方的某个位置 C# 有一种方法可以“抛出”来自System.Exception 对象,并“抓住”那些从更高的堆栈。另外,我认为.NET运行时报告每个抛出异常本身的一些问题。

  
      
  • 什么是例外?什么是它最低级别的内存组合?在.NET中,我可以将其视为某种异常类型的对象实例。在本土世界,它是由什么组成的?有些数据存在吗?
  •   

在C ++中,它只是一个任意对象,就像代码中的其他对象一样被创建:

throw 42;                     // throws an int object
throw "blah";                 // throws a char[5] object
throw std::string("arg!");    // throws a std::string object
throw my_type(42);            // throws a my_type object
throw std::exception("doh!"); // throws a std::exception object

抛出的异常与catch短语的匹配非常类似于重载函数的匹配。 (一个很大的区别是,短语是有序的。也就是说,第一个匹配将会“赢”并捕获对象。但是,重载函数必须始终提供明确的最佳匹配。)

  
      
  • 如果程序员没有明确抛出异常,谁会创建异常,如下面的代码所示?它是某些语言运行库提供的支持的一部分吗?
  •   

在C ++中,异常几乎只能从代码中抛出。它可以是您自己的代码,其他人的代码,某些库的代码或标准库的代码。但通常必须在某处发表throw声明。有一些例外(没有双关语),例如std::bad_alloc引发的new(可能是从代码中的throw语句引发的,但我认为没有必要从std::bad_cast抛出的dynamic_cast<> SomeException e = new SomeException(); throw e; 。 (此外,下一个标准,C ++ 1x,预计明年,允许异常以某种方式跨线程边界,这可能需要标准库实现者找到一种方法来在一个线程中存储异常并从另一个线程重新抛出它。但我'我对此很朦胧。)

SomeException e; throw e;

在C ++中你可以,但你很少想要,抛出指针。你要么

throw SomeException();

std::exception
  
      
  • 工作范例有什么例外?是否真的发生了一些错误,语言运行时会创建相应数据结构/类型的实例来表示错误的详细信息?
  •   

C ++标准库抛出异常的地方很少,但除此之外,我只能想到上面提到的两个特性(加上C ++ 1x中的特性),运行时抛出异常“本身”。

  
      
  • 我们怎么能在运行时知道所有可能的意外情况,从而创建足够的异常数据结构/类型来表示它们?
  •   

在C ++中只抛出从std::exception派生的类的对象是很常见的,尽管我遇到的代码使用了自己的异常类层次结构,而这些层次结构并非基于OutOfRange。标准库中的异常层次结构的问题在于它的类非虚拟地相互派生,这使得不可能让自己的异常层次结构使用多重继承来遮蔽标准库中的异常层次结构。 (比如拥有继承自std::out_of_range的您自己的MyException异常类型以及继承自std::exception的异常基类{{1}}。)

C ++处理异常的方法主要基于以下原则:

  • 编写代码,使其免受任何时候抛出的异常的影响。使用RAII和其他技术来实现这一目标。
  • 仅在您可以对其进行操作的地方捕获例外。
  • 仅在特殊情况下抛出例外情况。不要将它们用于控制流程。 (C ++异常的设计目标是供应商能够实现它们,以便在非特殊情况下最小化开销,但需要以特殊情况为代价。)

答案 1 :(得分:1)

根本原因是异常是具有预定义处理机制的特殊情况。其余部分由实施完成。

类型就在那里,因为有一个对象存储特殊情况的额外数据是很方便的。也就是说,异常可能是一个受控开关,例如每次硬件无法执行操作时都会重启机器。

答案 2 :(得分:1)

异常实际上是一种错误处理方式,它会中断流,使其无法再继续。

例外有两个方面,投掷者和捕手。 thrower负责检测错误是否已发生,创建一个包含错误信息的异常类并“抛出”它。几乎就像把它放在一个球里扔球一样 - 此时它的工作已经完成,现在由“捕手”决定了。

捕获器是错误处理机制,它知道如何处理错误。您可能会发现捕手只知道如何处理某些类型的错误。这是由类型层次结构处理的,但在我看来,如果没有使用运行时类型信息,那会更好,因为这可能会导致开销。

在正常流程中使用异常通常是不可取的,例如,当您读取文件直到文件末尾不是错误但是例外情况时,应该使用正常流程处理,而不是例外。

答案 3 :(得分:0)

例外是处理错误情况的一种方式。替代方案是返回值,以及许多ifs和switch / cases。

确实,异常就像一个隐藏的goto,但最终代码更容易维护,然后它就是替代。

处理异常非常简单。代码中抛出的所有异常都应该有一个公共基类,它记录错误。所有例外都应该包含确切发生的信息。

同样不应该滥用异常,因为它们应该为每一件小事扔掉它们。如果可能,应在本地处理错误。如果没有,则抛出一个异常,该异常将在可以处理异常的级别捕获。

最后一点:没有例外的c ++不是c ++,它是带有类

的c

答案 4 :(得分:0)

首先,我的答案仅适用于C ++。 C没有例外,我对C#也不熟悉,也不能生成包含该语言的语句。

异常对象就是一个对象。它可以是某种类类型的对象,但它也可以很容易地成为整数。它可以在某种程度上与函数的返回值进行比较。最大的区别在于,由于异常处理中涉及的控制流,在堆栈上存储异常是有问题的。

在C ++中,所有异常始终是throw语句的结果。使用Microsoft SEH,它是创建SEH异常的语言运行时。因此,总会有一些代码创建异常对象。

在C ++中,语言运行库不必知道可抛出的所有可能的应用程序级异常。它只需要提供一种机制,catch - 处理程序可以访问(抛出的)异常对象的副本。如何实现这一点取决于编译器编写者。

答案 5 :(得分:0)

当代码想在计算机上做一个操作时,某些代码抛出异常就不可能。

因为我们知道如果它出现在客户端系统中,那么他就可以创建问题,因此他使用exceptionhandeling来处理异常。

这是一个有用的系列

http://social.msdn.microsoft.com/Search/en-IN?query=exception+handling&ac=8

答案 6 :(得分:0)

传统的错误处理方法存在严重问题:

  • 从方法中返回错误代码需要将每次调用包装到if中,以检查错误。仅这一点就会使代码库几乎翻倍,如果你忘记只执行一次,那么你的程序就不再健壮了。
  • 全局错误代码(C中的a-la errno)不是线程安全的。
  • 任何类型的错误代码都只是一个数字。很难传达关于实际错误的更复杂的细节。
  • 即使你使用“异常结构”,它们很难构成 - 不同的代码层可能会使用不同的结构。
  • 等等...

例外情况允许您:

  • 传达有关错误的丰富信息,因为您正在抛出真实对象而不仅仅是错误代码。对象的类型不必预先确定,并且可以在运行时“匹配”错误(实际上,C ++中RTTI的主要动机之一是确定抛出异常的运行类型类型)。
  • 从错误处理中解除错误检测。错误检测是在一段代码中完成的,该代码对错误有很多了解(在调用层次结构的底部),而错误处理可以在更高层的代码中完成,可能是知道UI的一部分。< / LI>
  • 之间的代码层不必知道任何这些(但它们确实需要以“异常安全”的方式编写)。

  

什么是例外?

一个对象。

  

谁创建例外......?

在C ++中,对象在被抛出之前被复制,因为编译器需要在堆栈展开期间保证异常的生命周期,因为只要有任何catch块可以捕获它

在C#中,使用垃圾收集器,即使在堆栈展开期间,我们也可以保留原始对象。

  

工作范例有什么例外?

异常模型异常事件。如果事情经常发生,那么可能也不例外。

如果合适,每种错误都可以由不同类的实例表示。或者您可以用同一个类表示每个错误。或者介于两者之间。没有严格的规则。

  

我们怎样才能在运行时知道所有可能的意外情况,从而创建足够的异常数据结构/类型来表示它们?

您知道 代码如何失败,因此您可以适当地设计您的例外。在其他人写的代码中,假设它可以随时抛出任何东西,除非另有证明。