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

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


当前回答

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

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

其他回答

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

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

这总是很危险的,特别是当你在构造函数中分配资源时;根据你的语言,析构函数不会被调用,所以你需要手动清理。这取决于在您的语言中对象的生命周期何时开始。

我真正这么做的唯一一次是在某个地方出现了安全问题,这意味着对象不应该,而不是不能,被创建。

There is generally nothing to be gained by divorcing object initialization from construction. RAII is correct, a successful call to the constructor should either result in a fully initialized live object or it should fail, and ALL failures at any point in any code path should always throw an exception. You gain nothing by use of a separate init() method except additional complexity at some level. The ctor contract should be either it returns a functional valid object or it cleans up after itself and throws.

考虑一下,如果实现了单独的init方法,仍然必须调用它。它仍然有可能抛出异常,它们仍然必须被处理,而且它们实际上总是必须在构造函数之后立即被调用,除了现在你有4种可能的对象状态而不是2种(IE,已构造,初始化,未初始化,失败vs只是有效和不存在)。

In any case I've run across in 25 years of OO development cases where it seems like a separate init method would 'solve some problem' are design flaws. If you don't need an object NOW then you shouldn't be constructing it now, and if you do need it now then you need it initialized. KISS should always be the principle followed, along with the simple concept that the behavior, state, and API of any interface should reflect WHAT the object does, not HOW it does it, client code should not even be aware that the object has any kind of internal state that requires initialization, thus the init after pattern violates this principle.

构造函数的任务是使对象进入可用状态。关于这个问题,基本上有两种观点。

一组人赞成两阶段建设。构造函数只是将对象带入一个休眠状态,在这种状态下它拒绝做任何工作。还有一个额外的函数来进行实际的初始化。

我一直不明白这种方法背后的原因。我坚决支持单阶段构造,即对象在构造后完全初始化并可用。

如果单阶段构造函数未能完全初始化对象,则应该抛出。如果对象不能初始化,则必须不允许它存在,因此构造函数必须抛出。

参见c++常见问题解答第17.2和17.4节。

一般来说,我发现如果构造函数被编写,那么它们就不会失败,那么移植和维护结果的代码就会更容易,而可能失败的代码则放在一个单独的方法中,该方法返回错误代码并使对象处于惰性状态。