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

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


当前回答

OO中通常的约定是对象方法确实有功能。

因此,作为一个推论,永远不要从构造函数/init中返回僵尸对象。

僵尸没有功能,可能缺少内部组件。只是一个空指针异常等待发生。

很多年前,我第一次在Objective C中制作僵尸。

就像所有的经验法则一样,也有一个“例外”。

一个特定的接口完全有可能有这样的契约 存在一个允许触发异常的方法“initialize”。 在调用initialize之前,实现此接口的对象可能不会正确响应除属性设置器之外的任何调用。在引导过程中,我在OO操作系统中的设备驱动程序中使用了这种方法,而且它是可行的。

一般来说,你不需要僵尸对象。在Smalltalk这样的语言中,使用变得有点冒火,但是过度使用变得也是一种糟糕的风格。be允许一个对象在原地变成另一个对象,因此不需要信封包装器(高级c++)或策略模式(GOF)。

其他回答

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

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

如果无法在构造函数中初始化对象,则抛出异常,一个例子是非法参数。

根据一般经验,应该尽可能快地抛出异常,因为当问题的根源接近发出错误信号的方法时,这会使调试变得更容易。

我不确定是否有答案可以完全与语言无关。有些语言处理异常和内存管理的方式不同。

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#)

如果无法创建有效对象,则绝对应该从构造函数抛出异常。这允许您在类中提供适当的不变量。

在实践中,你可能必须非常小心。记住,在c++中,析构函数不会被调用,所以如果你在分配资源后抛出,你需要非常小心地正确处理它!

本页对c++中的情况进行了全面的讨论。

Throwing an exception during construction is a great way to make your code way more complex. Things that would seem simple suddenly become hard. For example, let's say you have a stack. How do you pop the stack and return the top value? Well, if the objects in the stack can throw in their constructors (constructing the temporary to return to the caller), you can't guarantee that you won't lose data (decrement stack pointer, construct return value using copy constructor of value in stack, which throws, and now have a stack that just lost an item)! This is why std::stack::pop does not return a value, and you have to call std::stack::top.

这个问题在这里有很好的描述,检查第10项,编写异常安全的代码。