When is it right for a constructor to throw an exception?

前端 未结 24 820
时光取名叫无心
时光取名叫无心 2020-11-30 16:47

When is it right for a constructor to throw an exception? (Or in the case of Objective C: when is it right for an init\'er to return nil?)

It seems to me that a cons

相关标签:
24条回答
  • 2020-11-30 17:28

    Note that if you throw an exception in an initializer, you'll end up leaking if any code is using the [[[MyObj alloc] init] autorelease] pattern, since the exception will skip the autorelease.

    See this question:

    How do you prevent leaks when raising an exception in init?

    0 讨论(0)
  • 2020-11-30 17:28

    The best advice I've seen about exceptions is to throw an exception if, and only if, the alternative is failure to meet a post condition or to maintain an invariant.

    That advice replaces an unclear subjective decision (is it a good idea) with a technical, precise question based on design decisions (invariant and post conditions) you should already have made.

    Constructors are just a particular, but not special, case for that advice. So the question becomes, what invariants should a class have? Advocates of a separate initialization method, to be called after construction, are suggesting that the class has two or more operating mode, with an unready mode after construction and at least one ready mode, entered after initialization. That is an additional complication, but acceptable if the class has multiple operating modes anyway. It is hard to see how that complication is worthwhile if the class would otherwise not have operating modes.

    Note that pushing set up into a separate initialization method does not enable you to avoid exceptions being thrown. Exceptions that your constructor might have thrown will now be thrown by the initialization method. All the useful methods of your class will have to throw exceptions if they are called for an uninitialized object.

    Note also that avoiding the possibility of exceptions being thrown by your constructor is troublesome, and in many cases impossible in many standard libraries. This is because the designers of those libraries believe that throwing exceptions from constructors is a good idea. In particular, any operation that attempts to acquire a non shareable or finite resource (such as allocating memory) can fail, and that failure is typically indicated in OO languages and libraries by throwing an exception.

    0 讨论(0)
  • 2020-11-30 17:29

    Using factories or factory methods for all object creation, you can avoid invalid objects without throwing exceptions from constructors. The creation method should return the requested object if it's able to create one, or null if it's not. You lose a little bit of flexibility in handling construction errors in the user of a class, because returning null doesn't tell you what went wrong in the object creation. But it also avoids adding the complexity of multiple exception handlers every time you request an object, and the risk of catching exceptions you shouldn't handle.

    0 讨论(0)
  • 2020-11-30 17:30

    To me it's a somewhat philosophical design decision.

    It's very nice to have instances which are valid as long as they exist, from ctor time onwards. For many nontrivial cases this may require throwing exceptions from the ctor if a memory/resource allocation can't be made.

    Some other approaches are the init() method which comes with some issues of its own. One of which is ensuring init() actually gets called.

    A variant is using a lazy approach to automatically call init() the first time an accessor/mutator gets called, but that requires any potential caller to have to worry about the object being valid. (As opposed to the "it exists, hence it's valid philosophy").

    I've seen various proposed design patterns to deal with this issue too. Such as being able to create an initial object via ctor, but having to call init() to get your hands on a contained, initialized object with accesors/mutators.

    Each approach has its ups and downs; I have used all of these successfully. If you don't make ready-to-use objects from the instant they're created, then I recommend a heavy dose of asserts or exceptions to make sure users don't interact before init().

    Addendum

    I wrote from a C++ programmers perspective. I also assume you are properly using the RAII idiom to handle resources being released when exceptions are thrown.

    0 讨论(0)
  • 2020-11-30 17:32

    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.

    This problem is well described here, check Item 10, writing exception-safe code.

    0 讨论(0)
  • 2020-11-30 17:34

    It's always pretty dodgy, especially if you're allocating resources inside a constructor; depending on your language the destructor won't get called, so you need to manually cleanup. It depends on how when an object's lifetime begins in your language.

    The only time I've really done it is when there's been a security problem somewhere that means the object should not, rather than cannot, be created.

    0 讨论(0)
提交回复
热议问题