Question

In what seems like a deliberate design decision, C++ does not have a null value for objects and references. This makes using objects and references very elegant since we don't have to perform null checks. It also means constructors must always return an object:

void main () {
    // o is guaranteed to be non-null by c++:
    Object o = new Object();         
}

// Allowing this hypothetical "failing" constructor would break the guarantee:
Object() { return void }

Sometimes object creation has to fail, an easy example being a Connection object. Since C++ does not allow this, programmers have developed alternatives: initialization methods (with various names), static builder methods, "Factories" that do not use any polymorphism, and null objects. One reason for adding exceptions may have been as a way to go around this limitation of C++ constructors, but either way, it is a tradeoff - we avoid null checks but sometimes have awkward construction.


That explains why C++ does not allow constructors to return null. But it is insufficient when we consider newer OOP languages like Java and C#. There, references are allowed to be null, yet constructors work like C++ and must not fail. So we get to write both null checks and alternative construction schemes (having to write factories, init methods etc). What benefits does this bring?

Was it helpful?

Solution

The standard method of handling a constructor that fails is to throw an exception. Constructors do not typically fail, and it's usually easier to handle such failures in a separate place rather than to clutter the main logic.

If a Connection constructor fails, whatever was supposed to happen with that Connection isn't going to happen, so throwing rather than executing the rest of the function or try...catch block is appropriate.

If you want an object that might or might not exist, consider std::optional (or boost::optional if your C++ compiler is older).

OTHER TIPS

The case of C++

In C++ constructors are expected to succeed. The only way for a constructor to fail is to raise an exception.

Bjarne Stroustrup confirms in his book "The design and evolution of C++" that this is one of the most important aspect of exceptions (Chapter 16.5.1: Errors in constructors), as you rightly pointed out in your question.

Now imagine for a second that a constructor could fail without exceptions...

The pointer initialisation Object *o = new Object; could work with this semantic without problem with a nullptr. In fact, it is possible to get this semantic if the failure is caused by an allocation issue with nothrow.

But how could you handle the following cases:

  1. Construction of local (value) object: Object o; invokes for example a default constructor. If failed construction would be possible, you or the compiler would have to check for a void object in all subsequent uses of o. In most cases this would be a performance overhead that is justified only by some exceptional cases, which is not the design philosophy of C++
  2. Construction of a class member : for example class X { public: Object o; };. If construction of o would fail, in this simple example you could simply imagine to say that the class X object construction fails as well. But how should it work if class X would have several different members, some of them with a successful construction, and some of them with a failed object ? And what if there would be some other pointer members where the compiler cannot determine if the pointed object should be destroyed or not in case of a partially failed construction of X ?
  3. Construction of a derived class: what to do if a base class object could be successfully constructed, but the derived class object not: should the bbase class object be returned ? Or should it be destroyed ?
  4. I don't mention multiple inheritance or virtual inheritance which have similar problems.

If C++ would allow constructors to fail, there would be a lot of unsolved semantic issues, or a lot of overhead that is useless most of the time. This explains the current design option: handle exceptional circumstances exceptionally.

By the way, Object o = new Object(); is not valid in C++.

Code design issue ?

If you feel you have the need for a constructor that is expected to fail as a normal behavior, there is certainly a design issue.

May be you should then consider to use an object with a special fail state. You would then consider again that construction is always successful, but that the state of the object might not be the one that is expected. You could then check whenever needed if the state of the object is ok or not.

Java and C# cases

Java and C# also expect the constructor to succeed or to raise an exception. The reasons are very similar to those mentioned for C++. Although case 1 would not be applicable because objects are managed by reference, and case 4 not relevant because these languages do not support MI, the case 2 and 3 would cause semantic issues in case of partially created objects.

If the language was to allow constructor failure without exception, it would have to decide how to handle partially successful construction (either keep the correct member objects and let the other be nil, or destroy constructed members and return nil). But the chosen semantic might not be the correct one in every case. Exception handling allows more flexibility in this regard.

As a solution to the design smell, in addition to the special fail state already mentioned for C++, you could also easily use a factory or a builder that could return nil, for example if the construction parameter are invalid.

Your example using C++ won't work because C++ has stack objects in addition to pointers.

int main
{ 
  Object x;
  x = nullptr; // x is not a pointer, this makes no sense.
}

Also, void is not a value, so you can't return or assign void. The very existence of stack variables makes the concept of a void-returning constructor nonsense. In another language that didn't allow stack objects, you could do imagine doing it:

Class OPQ
{
  Constructor(bool b) { if (b == false) return null; else return this; }
}

and then perhaps garbage collection would clean up your OPQ when the constructor exits. This means your caller would have to handle null results, but on the other hand this means such a construction would work in an exception-free programming language. (perhaps one where null-object operations are no-ops.)

So I think bottom line, there's no compulsion either way in general. C++ automatic variables preclude the notion, but a reference-based language can do as it likes. Likely C# and Java chose as they did because the language makers liked exceptions and wanted to imitate C++ to some extent.

Swift has no problems at all with this. Instances of classes are reference objects, you can have optional classes, so you can have initialisers that can’t fail, or failable initialisers. The implementation of a failable initialiser is somehow tricky since the language must clean up any half initialised object correctly, but that’s invisible to the programmer.

The good thing in Swift is that you can’t just ignore the fact that an optional object could be nil.

Licensed under: CC-BY-SA with attribution
scroll top