构造函数何时抛出异常是正确的?(或者在Objective C的情况下:什么情况下init ` er才应该返回nil?)
在我看来,如果对象不完整,构造函数应该失败——因此拒绝创建对象。也就是说,构造函数应该与它的调用者有一个合同,以提供一个函数和工作对象,在哪些方法可以被有意义地调用?这合理吗?
构造函数何时抛出异常是正确的?(或者在Objective C的情况下:什么情况下init ` er才应该返回nil?)
在我看来,如果对象不完整,构造函数应该失败——因此拒绝创建对象。也就是说,构造函数应该与它的调用者有一个合同,以提供一个函数和工作对象,在哪些方法可以被有意义地调用?这合理吗?
当前回答
参见c++常见问题解答第17.2和17.4节。
一般来说,我发现如果构造函数被编写,那么它们就不会失败,那么移植和维护结果的代码就会更容易,而可能失败的代码则放在一个单独的方法中,该方法返回错误代码并使对象处于惰性状态。
其他回答
构造函数的任务是使对象进入可用状态。关于这个问题,基本上有两种观点。
一组人赞成两阶段建设。构造函数只是将对象带入一个休眠状态,在这种状态下它拒绝做任何工作。还有一个额外的函数来进行实际的初始化。
我一直不明白这种方法背后的原因。我坚决支持单阶段构造,即对象在构造后完全初始化并可用。
如果单阶段构造函数未能完全初始化对象,则应该抛出。如果对象不能初始化,则必须不允许它存在,因此构造函数必须抛出。
如果无法在构造函数中初始化对象,则抛出异常,一个例子是非法参数。
根据一般经验,应该尽可能快地抛出异常,因为当问题的根源接近发出错误信号的方法时,这会使调试变得更容易。
如果无法创建有效对象,则绝对应该从构造函数抛出异常。这允许您在类中提供适当的不变量。
在实践中,你可能必须非常小心。记住,在c++中,析构函数不会被调用,所以如果你在分配资源后抛出,你需要非常小心地正确处理它!
本页对c++中的情况进行了全面的讨论。
参见c++常见问题解答第17.2和17.4节。
一般来说,我发现如果构造函数被编写,那么它们就不会失败,那么移植和维护结果的代码就会更容易,而可能失败的代码则放在一个单独的方法中,该方法返回错误代码并使对象处于惰性状态。
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/