views:

542

answers:

10

Please note that this is asking a question about constructors, not about classes which handle time.

Suppose I have a class like this:

class Time
{
protected:
    unsigned int m_hour;
    unsigned int m_minute;
    unsigned int m_second;
public:
    Time(unsigned int hour, unsigned int minute, unsigned int second);
};

While I would want a to be constructed successfully, I would want the constructor of b to fail.

Time a = Time(12,34,56);
Time b = Time(12,34,65); // second is larger than 60

However, this is not possible, because constructors do not return any values and will always succeed.

How would the constructor tell the program that it is not happy? I have thought of a few methods:

  1. have the constructor throw an exception, and have handlers in the calling function to handle it.
  2. have a flag in the class and set it to true only if the values are acceptable by the constructor, and have the program check the flag immediately after construction.
  3. have a separate (probably static) function to call to check the input parameters immediately before calling the constructor.
  4. redesign the class so that it can be constructed from any input parameters.

Which of these methods is most common in industry? Or is there anything I may have missed?

+30  A: 

The typical solution is to throw an exception.

The logic behind that is the following: the constructor is a method that transforms a chunk of memory into a valid object. Either it succeeds (finishes normally) and you have a valid object or you need some non-ignorable indicator of a problem. Exceptions are the only way to make the problem non-ignorable in C++.

sharptooth
Thank you for your clarification.
Andy
the answer below for making the ctor arguments hour, minute and second types is a lot better than allowing ints. Throwing exceptions for this is ludicrous. You can catch this at compile time.
Tim
@tim: Of course not that is why we use exceptions. Admittedly this example is a bit contrived (and there are other workarounds for this problem). But in the general case if a constructor has bad inputs you MUST throw an exception. The other choice is to have an invalid object. An invalid object will not obey any constraints applied to its methods. So the real solution here: inputs from the user are validated (bad data generates an error message) then used in the constructor (which will throw if the validation failed (which is good)).
Martin York
+9  A: 

There is one more possible way. I am not saying this is in any way preferred, only adding it for completeness:

Create a factory function that creates an instance of your class on the heap, and returns a null pointer if the creation fails.

This is not really appropriate with valuetype-like objects as dates, but there might be useful applications.

Timbo
Thank you for your idea. It will be useful in some circumstances as you said.
Andy
In C++ the most obvious implementation of this factory function is the nothrow form of operator new. i.e. do only heap allocation. This is one of many reasons virtually every other language allocates objects only on the heap. (Not advocacy, just food for thought.)
quark
+1  A: 

First one is the best, exceptions are the best way to inform the class users about the errors.

it is not recommended another way because if the constructor returns without erros, it means you have constructed an object correctly and you can use it anywhere

Ahmed Said
+4  A: 

"Exception thrown from a C'tor" is not a four-letter word. If an object can not be created correctly, the C'tor should fail, because you'd rather fail in construction than having an invalid object.

Gal Goldman
I completely agree, a lot of c++ developers wrongly think that the results of exceptions in constructors are undefined and adopt two phase construction with an initialise method.
iain
+9  A: 

Normally I'd say (1). But if you find that callers are all surrounding the construction with try/catch, then you can provide the static helper function in (3) as well, since with a bit of preparation the exception can be made impossible.

There is another option, although it has significant consequences for coding style so should not be adopted lightly,

5) Don't pass the parameters into the constructor:

class Time
{
protected:
    unsigned int m_hour;
    unsigned int m_minute;
    unsigned int m_second;
public:
    Time() : m_hour(0), m_minute(0), m_second(0) {}
    // either return success/failure, or return void but throw on error,
    // depending on why the exception in constructor was undesirable.
    bool Set(unsigned int hour, unsigned int minute, unsigned int second);
};

It's called two-phase construction, and is used precisely in situations where it is undesirable or impossible for constructors to throw exceptions. Code using nothrow new, to be compiled with -fno-exceptions, is probably the classic case. Once you get used to it, it is slightly less annoying than you might at first think.

Steve Jessop
Two-phase construction is bad, since the user of the class is bound to forget it. It esp interferes with the Immutable Value idiom that a class like `Time` should implement.
If the user of the class forgets it, as will very occasionally happen, the problem is with the user. Seriously, what made them think a 0-arg constructor can result in "quarter past three"?. It's only tricky in cases where the second phase is also 0-arg and just allocates resources. If you're using 2-phase anywhere, you should use it everywhere, so when the caller forgets it they treat the resulting debugging as a learning experience. I agree it's rubbish to have almost-immutable types, and option 5 is not the best for those. Like I say, I prefer 1 or 3.
Steve Jessop
Steve Jessop
I upvoted this question because imho it's the most logical solution. However in Flash new Date doesn't return a 0 date but the current date. This can make it a bit strange.
PoweRoy
+1  A: 

Just to elaborate a bit on the answers given by onebyone and Timbo. When people discuss the use of exceptions, usually someone eventually says: "Exceptions should be used in exceptional situations."

As you can tell from the most of the answers here, if a constructor fails then the correct response is to throw an exception. However, in your case, it is not necessarily that you cannot create the object, it's more that you don't want to create it.

Where the values are being read from an external source (eg. a file or a stream) there is a good chance that invalid values will be received and in that case, then it's not really an exceptional situation.

Personally, I would lean towards validating the arguments before constructing the time object (something like Timbo's answer) and I would then have an assertion in the constructor to verify that they arguments are valid.

In the case that your constructor needs a resource (eg. allocates memory) then that, IMHO, would be closer to an exceptional situation and so you would then throw an exception.

Richard Corden
+2  A: 
  • have the constructor throw an exception, and have handlers in the calling function to handle it.

Yes. Design by Contract and leave the precondition checking on, and in case of failure, throw an exception. No invalid times anymore.

  • have a flag in the class and set it to true only if the values are acceptable by the constructor, and have the program check the flag immediately after construction.

Maybe. Acceptable in complex cases but again, throw if your checking fails.

  • have a separate (probably static) function to call to check the input parameters immediately before calling the constructor.

Maybe. This is about telling whether input data are correct or not, and may be useful if telling that is nontrivial, but see above for how to react in case of invalid data.

  • redesign the class so that it can be constructed from any input parameters.

No. You would basically defer the problem.

Daniel Daranas
+3  A: 

Typically you'd have a private/protected constructor and a public static factory method to create the Time object. It is not a good idea to throw an exception from a constructor because it wreaks havoc on inheritance. This way your factory method can throw an exception if needed.

YES! Here is a CORRECT answer finally. Use a static method so that you call something like Time.createTime(12,34,65) and it can return null since the values are invalid, whereas Time.createTime(12,34,56) would return a new Time set to those values. (also don't forget to make your constructor private in this case)
Ricket
@Ricket: Doesn't this disallow the creation of automatic instances?
Éric Malenfant
@Manjit: Could you elaborate on the "wreaks havoc on inheritance" part?
Éric Malenfant
Of all the options discussed here, this is the one I've seen most (by a long shot). And yes, it does disallow automatic instances.
Darryl
+19  A: 

Another alternative, for completeness:

  • Redesign the interface so that invalid values are "impossible"

In your "Time" class, for example, you could have:

class Time{
public:
    Time(Hours h, Minutes m, Seconds s);
//...
};

Hours, Minutes and Seconds being bounded values. For example, with the (not yet)Boost Constrained Value library:

typedef bounded_int<unsigned int, 0, 23>::type Hours;
typedef bounded_int<unsigned int, 0, 59>::type Minutes;
typedef bounded_int<unsigned int, 0, 59>::type Seconds;
Éric Malenfant
I wish I could vote this up more times
Tim
John R. Strohm
@tim: And how do you plan to handle Hours(n) when n is 30? In that case the *Hours ctor* has to throw an exception! Working within the system, which in this case means ctors must either throw or succeed, is hard to avoid. (It's certainly possible for this to be a better overall solution---hard to tell from a contrived example---but the point is you don't avoid exceptions.)
Roger Pate
What Roger said.
Fenugreek Femtosecond
Don't forget about leap seconds.
Christoffer Hammarström
Well the comment about leap seconds might have been said as a joke but it make a good point: what happen when you extend the example to dates. Now you could not have a Day, Month, Year class because of the complex interaction between them. Still, it is a good answer for the current example.
n1ck
A: 

I don't think you have much choice.

If you got invalid input you can't do much than signal to the caller than it is invalid. You can then use exception or error code.

Error code in a constructor would need to be passed as a reference parameter and as such would look very akward.

You could try to find how you could validate the inputs at the source. Why are you getting invalid input exactly? How can the input be invalid, etc.

An example for your date class would be to force the user (user of your program) to enter a valid date only (by forcing him to enter it in a calendar type GUI, for example).

You could also try to create a method in your class to handle input validation.

With that, the user (programmer this time) can either call it before construction and be sure that the call will not fail or fall back on exception if the user did not validate it.

If performance is important and you do not want to call the validate function twice (user call it, then in the constructor), I think you could use the named constructor idiom to have a CheckedConstructor and an UncheckedConstructor.

This is beginning to be architectural overdoing though, I think.

In the end, it would depend on the class and use case.

n1ck