构造函数何时抛出异常是正确的?(或者在Objective C的情况下:什么情况下init ` er才应该返回nil?)
在我看来,如果对象不完整,构造函数应该失败——因此拒绝创建对象。也就是说,构造函数应该与它的调用者有一个合同,以提供一个函数和工作对象,在哪些方法可以被有意义地调用?这合理吗?
构造函数何时抛出异常是正确的?(或者在Objective C的情况下:什么情况下init ` er才应该返回nil?)
在我看来,如果对象不完整,构造函数应该失败——因此拒绝创建对象。也就是说,构造函数应该与它的调用者有一个合同,以提供一个函数和工作对象,在哪些方法可以被有意义地调用?这合理吗?
当前回答
是的,如果构造函数未能构建其内部部分之一,则它可以(通过选择)有责任抛出(并以某种语言声明)一个显式异常,这在构造函数文档中有适当的说明。
This is not the only option: It could finish the constructor and build an object, but with a method 'isCoherent()' returning false, in order to be able to signal an incoherent state (that may be preferable in certain case, in order to avoid a brutal interruption of the execution workflow due to an exception) Warning: as said by EricSchaefer in his comment, that can bring some complexity to the unit testing (a throw can increase the cyclomatic complexity of the function due to the condition that triggers it)
如果它因为调用者而失败(比如调用者提供了一个空参数,而被调用的构造函数需要一个非空参数),构造函数无论如何都会抛出一个未检查的运行时异常。
其他回答
这总是很危险的,特别是当你在构造函数中分配资源时;根据你的语言,析构函数不会被调用,所以你需要手动清理。这取决于在您的语言中对象的生命周期何时开始。
我真正这么做的唯一一次是在某个地方出现了安全问题,这意味着对象不应该,而不是不能,被创建。
构造函数抛出异常是合理的,只要它正确地清理了自己。如果您遵循RAII范式(资源获取即初始化),那么构造函数通常会做有意义的工作;如果构造函数不能完全初始化,那么编写良好的构造函数将自行清理。
我不确定是否有答案可以完全与语言无关。有些语言处理异常和内存管理的方式不同。
I've worked before under coding standards requiring exceptions never be used and only error codes on initializers, because developers had been burned by the language poorly handling exceptions. Languages without garbage collection will handle heap and stack very differently, which may matter for non RAII objects. It is important though that a team decide to be consistent so they know by default if they need to call initializers after constructors. All methods (including constructors) should also be well documented as to what exceptions they can throw, so callers know how to handle them.
我通常支持单阶段构造,因为很容易忘记初始化对象,但也有很多例外。
Your language support for exceptions isn't very good. You have a pressing design reason to still use new and delete Your initialization is processor intensive and should run async to the thread that created the object. You are creating a DLL that may be throwing exceptions outside it's interface to an application using a different language. In this case it may not be so much an issue of not throwing exceptions, but making sure they are caught before the public interface. (You can catch C++ exceptions in C#, but there are hoops to jump through.) Static constructors (C#)
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/
OO中通常的约定是对象方法确实有功能。
因此,作为一个推论,永远不要从构造函数/init中返回僵尸对象。
僵尸没有功能,可能缺少内部组件。只是一个空指针异常等待发生。
很多年前,我第一次在Objective C中制作僵尸。
就像所有的经验法则一样,也有一个“例外”。
一个特定的接口完全有可能有这样的契约 存在一个允许触发异常的方法“initialize”。 在调用initialize之前,实现此接口的对象可能不会正确响应除属性设置器之外的任何调用。在引导过程中,我在OO操作系统中的设备驱动程序中使用了这种方法,而且它是可行的。
一般来说,你不需要僵尸对象。在Smalltalk这样的语言中,使用变得有点冒火,但是过度使用变得也是一种糟糕的风格。be允许一个对象在原地变成另一个对象,因此不需要信封包装器(高级c++)或策略模式(GOF)。