构造函数何时抛出异常是正确的?(或者在Objective C的情况下:什么情况下init ` er才应该返回nil?)

在我看来,如果对象不完整,构造函数应该失败——因此拒绝创建对象。也就是说,构造函数应该与它的调用者有一个合同,以提供一个函数和工作对象,在哪些方法可以被有意义地调用?这合理吗?


当前回答

构造函数抛出异常是合理的,只要它正确地清理了自己。如果您遵循RAII范式(资源获取即初始化),那么构造函数通常会做有意义的工作;如果构造函数不能完全初始化,那么编写良好的构造函数将自行清理。

其他回答

严格地从Java的角度来说,任何时候初始化使用非法值的构造函数时,它都应该抛出异常。这样它就不会被构造成一个糟糕的状态。

由于部分创建的类可能导致的所有麻烦,我认为永远不会。

如果需要在构造过程中验证某些内容,请将构造函数设为私有并定义一个公共静态工厂方法。如果某些东西无效,该方法可以抛出。但是如果一切都检查出来了,它就调用构造函数,保证不会抛出。

我不能说明Objective-C中的最佳实践,但在c++中,构造函数抛出异常是可以的。特别是没有其他方法可以确保在不调用isOK()方法的情况下报告构造过程中遇到的异常情况。

函数try块特性是专门为支持构造函数成员初始化失败而设计的(尽管它也可以用于常规函数)。这是修改或丰富将要抛出的异常信息的唯一方法。但由于其最初的设计目的(在构造函数中使用),它不允许空catch()子句包含异常。

Eric Lippert说有四种例外。

Fatal exceptions are not your fault, you cannot prevent them, and you cannot sensibly clean up from them. Boneheaded exceptions are your own darn fault, you could have prevented them and therefore they are bugs in your code. Vexing exceptions are the result of unfortunate design decisions. Vexing exceptions are thrown in a completely non-exceptional circumstance, and therefore must be caught and handled all the time. And finally, exogenous exceptions appear to be somewhat like vexing exceptions except that they are not the result of unfortunate design choices. Rather, they are the result of untidy external realities impinging upon your beautiful, crisp program logic.

构造函数本身不应该抛出致命异常,但它执行的代码可能会导致致命异常。像“内存不足”这样的事情不是您可以控制的,但是如果它发生在构造函数中,嘿,它就发生了。

愚蠢的异常永远不应该出现在任何代码中,所以它们应该被清除。

构造函数不应该抛出恼人的异常(例如Int32.Parse()),因为它们没有非异常情况。

最后,应该避免外生异常,但如果在构造函数中执行的某些操作依赖于外部环境(如网络或文件系统),则抛出异常是合适的。

参考链接:https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/

对我来说,这是一个有点哲学的设计决策。

拥有实例是非常好的,只要它们存在就有效,从ctor时间开始。对于许多重要的情况,如果无法进行内存/资源分配,则可能需要从ctor抛出异常。

其他一些方法是init()方法,它本身存在一些问题。其中之一是确保init()实际被调用。

一个变体使用惰性方法在第一次调用访问器/突变器时自动调用init(),但这要求任何潜在的调用者都必须担心对象是否有效。(而不是“它存在,因此它是有效的哲学”)。

我也看到过处理这个问题的各种设计模式。例如,可以通过ctor创建初始对象,但必须调用init()来获得包含的、初始化的具有访问器/突变器的对象。

每种方法都有其利弊;我已经成功地使用了所有这些方法。如果不是在创建对象的那一刻就创建现成的对象,那么我建议使用大量的断言或异常,以确保用户在init()之前不会进行交互。

齿顶高

我是从c++程序员的角度写的。我还假设您正确地使用了RAII习惯用法来处理抛出异常时释放的资源。