Is throwing an exception from a constructor considered good practice?
sobes.tech AI
Answer from AI
Yes, this is a standard and often necessary practice.
If an error occurs during the construction of an object that prevents it from being in a correct and fully initialized state, throwing an exception is the only correct way to inform the calling code about it. An object from which an exception was thrown in the constructor is not considered fully created, and its destructor will not be called automatically during stack unwinding.
This is important because:
- It prevents the creation of incorrect, partially initialized objects.
- It signals an initialization error that the calling code can handle.
- It adheres to the RAII (Resource Acquisition Is Initialization) principle, where successful constructor completion guarantees the correct state of the object and resource ownership.
At the same time, care must be taken with resources that were successfully allocated or initialized before the exception was thrown. These should be released or rolled back inside a try-catch block within the constructor itself or by using RAII wrappers (e.g., smart pointers) that automatically clean up resources upon scope exit (even during stack unwinding caused by an exception).
Example:
#include <stdexcept>
#include <new>
#include <iostream>
class MyClass {
public:
// Constructor that may throw an exception
MyClass(size_t size) : data_(nullptr) {
if (size == 0) {
// Throw an exception if size is invalid
throw std::invalid_argument("Size cannot be zero");
}
try {
// Attempt to allocate memory. new may throw std::bad_alloc
data_ = new int[size];
std::cout << "Memory allocated." << std::endl;
} catch (const std::bad_alloc& e) {
// Rethrow the exception after any handling
std::cerr << "Memory allocation failed: " << e.what() << std::endl;
throw; // Rethrow the caught exception
}
// If everything succeeded, the object is fully constructed
std::cout << "Object constructed successfully." << std::endl;
}
// Destructor is called only for fully constructed objects
~MyClass() {
std::cout << "Destructor called." << std::endl;
delete[] data_;
}
private:
int* data_;
};
int main() {
try {
std::cout << "Attempting to create object with size 10..." << std::endl;
MyClass obj1(10); // Successfully created
// Destructor for obj1 will be called upon try block exit
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
std::cout << std::endl;
try {
std::cout << "Attempting to create object with size 0..." << std::endl;
MyClass obj2(0); // Will throw an exception
// Destructor for obj2 will NOT be called, as constructor did not complete
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return 0;
}