Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Ramifications of ignoring exception from constructor

I've searched SO for an answer to this, but haven't found one.

When an object throws an exception at the end of the constructor, is the object valid or is this one of those 'depends on the construction technique'?

Example:

    struct Fraction
    {
      int m_numerator;
      int m_denominator;
      Fraction (double value,
                int denominator);
    };
    Fraction::Fraction(double value, int denominator)
    :  m_numerator(0), m_denominator(denominator)
    {
      if (denominator == 0)
      {
/* E1 */        throw std::logic_error("Denominator is zero.");
      }
      m_numerator = static_cast<int>(value * static_cast<double>(denominator));
      double actual_value = 0.0;
      actual_value = static_cast<double>(m_numerator) / static_cast<double>(m_denominator);
      double error = fabs(actual_value - value);
      if (error > 5.0E-5)
      {
/* E2 */  throw std::logic_error("Can't represent value in exact fraction with given denominator");
      }
    }

The program:

int main(void)
{
    try
    {
        Fraction f1(3.14159264, 4); // Throws exception, E2 above.
    }
    catch (...)
    {
        cerr << "Fraction f1 not exactly representable as fraction with denom. of 4.\n";
    }

    // At this point, can I still use f1, knowing that it is an approximate fraction?

    return EXIT_SUCCESS;
}

In this example, can f1 be used after the exception is caught, knowing that it is an approximate value?

The data members have been constructed and initialized.

I don't see any C++ language rule that is violated by the above.

Edit: Changed error delta value from 5.0E05 to 5.0E-5.

like image 206
Thomas Matthews Avatar asked Feb 04 '10 00:02

Thomas Matthews


People also ask

What happens if an exception is thrown in a constructor?

When throwing an exception in a constructor, the memory for the object itself has already been allocated by the time the constructor is called. So, the compiler will automatically deallocate the memory occupied by the object after the exception is thrown.

Can a constructor throws an exception How do you handle the error when the constructor fails?

Additionally, using constructors to their fullest can help you maintain clean, organized code. However, what happens if some of the data passed to the object through the constructor is invalid? Exception handling is the key. The short answer to the question “can a constructor throw an exception in Java” is yes!

Can we handle exception in constructor?

Yes, constructors are allowed to throw an exception in Java. A Constructor is a special type of a method that is used to initialize the object and it is used to create an object of a class using the new keyword, where an object is also known as an Instance of a class.

How should a constructor handle a failure?

The best way to signal constructor failure is therefore to throw an exception. If you don't have the option of using exceptions, the "least bad" work-around is to put the object into a "zombie" state by setting an internal status bit so the object acts sort of like it's dead even though it is technically still alive.


4 Answers

f1 is out of scope after the exception is caught. It no longer exists, so you can't use it.

like image 163
Jonathan M Davis Avatar answered Oct 13 '22 21:10

Jonathan M Davis


Jonathan's answer is correct. In addition, while the fraction may be in a valid state, I would not recommend using exceptions for flow control, and especially for communication about the state of an object. Instead, consider adding some kind of is_exactly_representable to your Fraction object API that returns a bool.

like image 38
fbrereto Avatar answered Oct 13 '22 19:10

fbrereto


No, once the scope that f1 is defined in exits, you can no longer use the object. So, in your code:

try
{
    Fraction f1(3.14159264, 4); // Throws exception, E2 above.

    // f1 can be used until here
}
catch (...)
{
}

// The scope that f1 was defined in is over, so the compiler will not let
// you reference f1

That said, perhaps you should rethink throwing an exception when you can't represent the actual value. Because that may only be applicable for certain uses, you could require the caller to request it:

enum FractionOption { disallowInexact, allowInexact };

Fraction::Fraction(double value, int denominator,
                   FractionOption option = disallowInexact)
{
    ...
    if ((option == disallowInexact) && (error > 5.0E-5))
    {
        throw std::logic_error("Can't represent value ...");
    }
}

Fraction f1(3.14159264, 4, allowInexact);
like image 43
R Samuel Klatchko Avatar answered Oct 13 '22 21:10

R Samuel Klatchko


throw in constructor = construction failed --> object unusable

As already noted, if the exception is thrown, then the object goes out of scope. However, you might be interested in the case when you allocate an object:

f = new Fraction(3.14159264, 4);

In this case, f is also unusable, because the constructor didn't finish work, and the pointer didn't get assigned. The destructor doesn't get called, and memory is deallocated, hence there's no way to use the object.

Hence, construct your object normally, don't use exceptions if you intend to use the class. Use a is_exact() member function to decide if it's exact after construction.

like image 29
Kornel Kisielewicz Avatar answered Oct 13 '22 21:10

Kornel Kisielewicz