How to prevent the constructor from creating an object when an exception is thrown

When the constructor throws an exception, how can I prevent the creation of an object?

In the example below, I create a Month () class for which the valid values โ€‹โ€‹of the int month_ are in the range of 1 to 12. I create December or dec with an integer value of 13. An exception is thrown, as it should, but the object is still created . Then the destructor is called.

How can I abort class instantiation when an exception occurs?

OUTPUT

 -- Month() constructor called for value: 2 -- Month() constructor called for value: 6 -- Month() constructor called for value: 13 EXCEPTION: Month out of range 2 6 13 -- ~Month() destructor called. -- ~Month() destructor called. -- ~Month() destructor called. Press any key to exit 

Minimal, complete and verifiable example

 #include <iostream> #include <string> class Month { public: Month(int month) { std::cout << "-- Month() constructor called for value: " << month << std::endl; try { // if ((month < 0) || month > 12) throw 100; Good eye, Nat! if ((month < 1) || month > 12) throw 100; } catch(int e) { if (e == 100) std::cout << "EXCEPTION: Month out of range" << std::endl; } month_ = month; } ~Month() { std::cout << "-- ~Month() destructor called." << std::endl; } int getMonth()const { return month_; } private: int month_; }; int makeMonths() { Month feb(2), jun(6), dec(13); std::cout << feb.getMonth() << std::endl; std::cout << jun.getMonth() << std::endl; std::cout << dec.getMonth() << std::endl; return 0; } int main() { makeMonths(); std::cout << "Press any key to exit"; std::cin.get(); return 0; } 
+7
c ++ constructor exception-handling c ++ 11 try-catch
source share
6 answers

How can I abort class instantiation when an exception occurs?

Well, you throw an exception in the constructor. But there is a catch: Do not catch it !

If you catch this, it will look like the exception never occurred. You caught him, so that he no longer goes up the call stack. Thus, the object is created because, as far as any person is concerned, the constructor did not throw an exception.

If you remove the catch clause from the constructor, you will probably get something like:

 -- Month() constructor called for value: 2 -- Month() constructor called for value: 6 -- Month() constructor called for value: 13 terminate called after throwing an instance of 'int' [1] 28844 abort (core dumped) ./main 

Here, the constructor threw an exception, and this time it went up the call stack outside the constructor because no one caught it in the constructor. Then it goes to makeMonths , where it also doesn't get there, and then to main , where it doesn't get either, and so the program aborts abnormally.

+15
source share

By default, throwing an exception in the constructor should prevent the destructor from being called. However, you catch the exception and handle it.

You can create a new exception inside your catch, which can then be seen outside this area so that the destructor is not called.

+10
source share

How can I abort class instantiation when an exception occurs?

You just (re) throw an exception, instead of catching it, I would recommend throwing a std::invalid_argument or std::out_of_range exception:

 class Month { public: Month(int month) { std::cout << "-- Month() constructor called for value: " << month << std::endl; if ((month < 0) || month > 12) throw std::invalid_argument("month"); // or throw std::out_of_range("month"); else month_ = month; } ~Month() { std::cout << "-- ~Month() destructor called." << std::endl; } int getMonth()const { return month_; } private: int month_; }; 

The created Month instance will be correctly deleted using the stack expansion mechanism. The created instance will never be created, so the destructor is not called at all.

See Live Example .


To make the exception more informative, you can use something in these lines:

  if ((month < 0) || month > 12) { throw std::out_of_range("'month' parameter must be in the range of 1-12."); } 

Also, if you do not like to initialize a member variable in the constructor body (as I usually do)

You can simply enter the lambda expression for the validation code:

 auto month_check = [](int month) { if ((month < 0) || month > 12) { throw std::out_of_range("'month' parameter must be in the range of 1-12."); } return month; }; class Month { public: Month(int month) : month_(month_check(month)) { std::cout << "-- Month() constructor called for value: " << month << std::endl; } ~Month() { std::cout << "-- ~Month() destructor called." << std::endl; } int getMonth()const { return month_; } private: int month_; }; 

Live demo

+4
source share

You should throw an exception, for example, in main, the message about the constructor should be in the try block. So ... 2. objects are created and when 3 objects are created, the throw 100 exception is processed mainly. I think this is one of many possibilities.

 #include <iostream> #include <exception> #include <iostream> #include <string> class Month { public: Month(int month) { try { if ((month < 0) || month > 12) throw 100; std::cout << "-- Month() constructor called for value: " << month << std::endl; } ~Month() { std::cout << "-- ~Month() destructor called." << std::endl; } int getMonth()const { return month_; } private: int month_; }; int makeMonths() { Month feb(2), jun(6), dec(13); std::cout << feb.getMonth() << std::endl; std::cout << jun.getMonth() << std::endl; std::cout << dec.getMonth() << std::endl; return 0; } int main() { try { makeMonths(); std::cout << "Press any key to exit"; std::cin.get(); } catch (...) { std::cout << "exception" << std::endl; } return 0; } 

OUTPUT:

 -- Month() constructor called for value: 2 -- Month() constructor called for value: 6 -- ~Month() destructor called. -- ~Month() destructor called. exception 
+1
source share

You must exclude the exception from the constructor and catch it in a code different from the constructor, for example, if the creation of an object is sued.

If the constructor ends by throwing an exception, then the memory associated with the object itself is cleared - there is no memory leak

From iso / cpp 1 source

If the constructor throws an exception, the object destructor does not start.

From iso / cpp 2 source

+1
source share

You can use the factory method template to avoid calling the constructor if the input is invalid.

The bottom line:

  • Create a static method called .New() or something else.

  • External objects call .New() instead of the constructor.

  • .New() checks the input.

    • If the input is good, then it calls the constructor and returns the result.

    • Otherwise, it throws an exception. Or you can return null or return the default value of << 24>.


 #include <iostream> #include <string> class Month { public: static Month New(int month) { std::cout << "-- Month.New() factory method called for value: " << month << std::endl; if (month < 0 || month >= 12) { std::cout << "-- Month.New() factory method found that month was invalid; throwing exception" << month << std::endl; throw /*exception type here*/; } return Month(month); } ~Month() { std::cout << "-- ~Month() destructor called." << std::endl; } int getMonth()const { return month_; } private: int month_; Month(int month) { month_ = month; } }; int makeMonths() { Month feb(2), jun(6), dec(13); std::cout << feb.getMonth() << std::endl; std::cout << jun.getMonth() << std::endl; std::cout << dec.getMonth() << std::endl; return 0; } int main() { makeMonths(); std::cout << "Press any key to exit"; std::cin.get(); return 0; } 
+1
source share

All Articles