I know that it's recommended not to throw an exception in constructor, but I need to use switch case in constructor, I'm wondering how should I handle with default case, which actually shouldn't be accessed.
enum LengthUnit {
MILLIMETERS = 0,
MICROMETERS,
NANOMETERS,
};
class Length {
public:
constexpr Length(double value, LengthUnit unit) noexcept {
switch(unit) {
case MILLIMETERS: mNanometers = value * 1e6; break;
case MICROMETERS: mNanometers = value * 1e3; break;
case NANOMETERS: mNanometers = value; break;
default: {
// accessed when called Length(1, LengthUnit(10)), not permitted
// throw std::exception();
}
}
private:
int mNanometers;
};
- constexpr is used to calc value when compile for acceleration
- noexcept is marked for constrcutor shouldn't throw an exception
CodePudding user response:
Your knowledge is incorrect. It's often recommended to throw an exception from constructor when constructing failed. Catching exceptions is usually most convenient but not always. Convenience is often not the reason why we use C .
So if you do not want to throw from constructors then you should have some kind of "bad", "nonexistent" or "misconstructed" states of misconstructed objects (and so storage for those states in objects) and also handle those states explicitly in style if (length.isBad())
not by catching exceptions. That is often not recommended.
CodePudding user response:
If you throw exception in constructor, then the destructor will not be executed automatically. This is a problem if you have manual resource acquiring in the constructor that has to be manually released in the destructor. But it is not a problem to throw exception in constructor if you have automatic objects in the class, responsible for acquire/release resources. The destructors of automatic members will be invoked. If you need manual resource acquiring, make separate class that manages automatically the resource, and use it in your class.
A good idea, this is already suggested in comments, use enum class, which will guard your code at compile time:
enum class LengthUnit : int
{
MILLIMETERS = 0,
MICROMETERS,
NANOMETERS,
};
The other option, if you still need to not throw from constructor, there is still a way to indicate the invalid state of the object. Using a member variable. But your class as is does not have a problem with throwing exception from constructor. Also using state variable is redundant if you use enum class
:
class Length
{
public:
Length(double value, LengthUnit unit) noexcept
{
switch(unit)
{
case LengthUnit::MILLIMETERS: mNanometers = value * 1e6; break;
case LengthUnit::MICROMETERS: mNanometers = value * 1e3; break;
case LengthUnit::NANOMETERS: mNanometers = value; break;
default:
valid = false;
}
}
bool isValid(){return valid;}
bool isInvalid(){return !valid;}
private:
bool valid = true;
int mNanometers = 0;
};