当从构造函数抛出未处理的异常时会发生什么

时间:2012-12-21 01:14:38

标签: java c++ memory-leaks

从构造函数中抛出未处理的异常会发生什么?对于Java和C ++?会有内存泄漏吗?

8 个答案:

答案 0 :(得分:7)

你问,

  

“从构造函数中抛出未处理的异常会发生什么?对于Java和C ++?会有内存泄漏吗?“

未处理的异常是一个没有关联处理程序的异常。

在C ++中,任何未处理的异常都会终止程序。在这种情况下,未指定堆栈是否展开,即,可以根据编译器执行或不执行成功构造的局部变量的析构函数。抛出异常(例如在构造函数内部)是无关紧要的。

  

C ++11§15.3/ 9
  “如果找不到匹配的处理程序,则调用函数std::terminate();在对std::terminate()的调用是否为实现定义之前,堆栈是否已展开。“

Java中未处理的异常同样必然会终止程序,或者至少是当前线程,如果它不是主线程,但保证调用finally子句:

  

Java SE 7语言规范§11.3
  “如果找不到可以处理异常的catch子句,则终止当前线程(遇到异常的线程)。在终止之前,所有   finally条款已执行[...]“

由于程序终止,实际上程序本身没有内存泄漏,因为在实践中操作系统会在进程后清理。

但是,崩溃程序可能会在磁盘上留下临时文件,并且可能会泄漏服务器进程中的其他资源,包括这些服务器进程中的内存泄漏。

答案 1 :(得分:6)

对于Java:控制流返回给调用者,就像从常规方法抛出异常一样。没有内存泄漏(半构造的实例将被丢弃并被垃圾收集)

答案 2 :(得分:2)

好吧,至少在C ++中,一个未处理的异常会一直持续到它的main(),从而关闭你的程序。然后,操作系统将处理未释放的内存。

不确定是否能回答你的问题......?

所以,基本上,就像它是从任何其他函数抛出的那样。

答案 3 :(得分:1)

如果在构造函数中创建依赖对象,则可能会发生内存泄漏。

在任何语言/环境中,如果这些家属被外部实体引用而无法清除,则可能会导致泄露。

在JAVA和C#中,如果不依赖外部参与,则不会导致泄漏。垃圾收集器最终会清理干净。

在C ++中,如果没有外部引用dependents,这肯定会导致泄漏。

请参阅Jon的答案,了解更多可能性:Can constructors throw exceptions in Java?

答案 4 :(得分:0)

值得补充的是:

1)Java区分“已检查”和“未检查”异常

2)应该“检查”大多数用户定义的异常。这意味着代码甚至不会编译,除非调用链中的每个模块a)处理异常,或b)明确标记它可以“抛出”异常

答案 5 :(得分:0)

答案 6 :(得分:-1)

是否会出现内存泄漏取决于代码的编写方式。如果您编写“好”代码,则不应该有内存泄漏。但是完全有可能提出一个可怕的错误方案。

如果构造函数在构造函数中分配任何内容,那么事情可能会出错。

解决方案,一般是使用所谓的“两阶段构造”,因此构造函数本身非常简单并且“不会出错”。一旦构造了对象,就可以调用一个成员函数,该函数以可能失败的方式填充对象,然后你可以随意抛出异常,只要确保析构函数在某一点上运行,一切都应该运作良好。谨防“析构函数中的部分构造对象”tho' - 如果你的指针为NULL,或者如果还没有在析构函数的中途构造其他东西,会发生什么。

[上面的内容是“在我们回到main之前某处有一个处理程序,我们确实想做除了中止整个程序之外的其他事情”]。

答案 7 :(得分:-1)

两种语言(C ++或Java)的情况类似但不同。

当抛出异常时,它会向上传播回堆栈,寻找处理程序。在C ++或Java中,它可能永远不会找到一个,因此一直放回到开始并终止程序。在Java中,有一个检查异常的概念,强制执行某种异常处理(如果选中)。在C ++中,有一个异常规范的概念,但它是不切实际的(设计不合理)而且不应该使用,因此,在C ++中将所有异常都视为“未经检查”。

异常是否最终会终止程序,或者被引发到远离抛出位置的某个地方,导致这一过程的展开过程非常重要。如果它最终终止程序,那么,当然,OS没有内存泄漏,因为OS回收内存。你需要担心的是:

  • 如果异常最终在上游某处处理,则在展开期间内存泄漏;和,
  • 可能泄漏的其他类型的资源(例如,待处理的数据库操作,网络连接等),因为如果程序终止,操作系统将不会回收/撤消这些资源。

当堆栈展开时,在C ++中,简单地说,保证完全构造的每个堆栈绑定对象(包括正在构造的对象的数据成员或基类实例)将被立即销毁(即,确定性地),与它们的创建顺序完全相反。因此,只要所有资源直接绑定到对象的构造/销毁(也称为“RAII”),在展开过程中就不会有(内存或其他资源)泄漏,因为成功获取的每个资源都将被释放(除非在展开过程中释放资源失败,这是您需要小心处理的事情。)

在Java中,“堆栈”展开以相同的方式发生,除了不是立即销毁对象,它们被标记为丢弃(即,被垃圾收集),并最终在未来的某些不确定点被销毁。这保证没有内存泄漏,只要垃圾收集器保持活动足够长时间来完成它的工作,我不确定如果程序最终终止于未处理的异常(但在那一点无关紧要) 。 Java中的主要问题是其他资源。必须在finally块中释放这些资源。保证在展开期间执行finally块,但是,当然,它们必须包含用于释放在相应的try块中分配的资源的代码。只要程序员完成了他的工作,资源就不会泄漏。

从构造函数抛出异常的事实确实没有多大区别,并且基本规则与抛出异常时不泄漏资源的基本规则相同:

  • 在C ++中,将每个单个资源(内存或其他)绑定到单个对象,语言保证其余的,没有泄漏。这是资源获取初始化(RAII)的习惯用法。
  • 在Java中,确保在其自己的try-block中编写每个非内存资源获取,该try-block具有自己的finally-block,用于释放单个资源。

在这两种情况下,您都必须彻底释放资源(禁止扔掉)。