Middle+
What nuances should be considered if an exception is thrown from the constructor?
sobes.tech AI
Answer from AI
- Partially constructed object: If an exception is thrown from a constructor, the object is considered not fully constructed. Its destructor will not be called automatically.
- Resource leaks: Resources acquired before the exception is thrown and not released will lead to leaks. It is necessary to use RAII (Resource Acquisition Is Initialization) mechanisms to manage resources.
- Order of member initialization: Class members are initialized in the order of their declaration, not in the order of initialization in the constructor's initializer list. If a member's initialization throws an exception, members initialized before it will be destroyed (if they have a destructor).
- Unsafe nested exceptions: Re-throwing an exception in a constructor's exception handler can lead to
std::terminate(). - Smart pointers: Using
std::unique_ptrorstd::shared_ptrto manage dynamically allocated memory inside a constructor ensures automatic cleanup when an exception is thrown. - Handling at the call site: The code creating the object should be wrapped in a
try-catchblock to catch exceptions from the constructor.
Example with RAII:
#include <iostream>
#include <fstream>
#include <stdexcept>
class MyClass {
private:
std::ofstream file;
public:
MyClass(const std::string& filename) : file(filename) {
if (!file.is_open()) {
// File did not open, throw exception BEFORE destructor is called
// std::ofstream destructor will close the file automatically if exception is thrown AFTER opening
throw std::runtime_error("Failed to open file: " + filename);
}
std::cout << "File successfully opened." << std::endl;
// Other initialization operations that may throw exceptions
}
// Destructor will be called only if constructor completes WITHOUT exception
~MyClass() {
std::cout << "Closing file." << std::endl;
file.close(); // std::ofstream destructor handles closing, but explicit close is also an option
}
// Method for demonstration
void write(const std::string& data) {
file << data << std::endl;
}
};
int main() {
try {
MyClass obj("output.txt");
// MyClass another_obj("non_existent_dir/another.txt"); // Example error
obj.write("Hello from MyClass!");
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return 0;
}