If the resource-allocation part of the constructor of e.g. a RAII socket wrapper fails, do I just throw an exception and be done with it? Or should I go with how std::fstream
seems to do it, where you need to check is_open()
after you construct the object?
The former seems more in-line with the name "resource allocation is initialization", but then why does the standard library basically make you check an error code before you use the object?
I am referring to the example on the reference page for basic_fstream
(paraphrased, notes added):
int main() {
std::string filename = "test.bin";
std::fstream s(filename);
if (!s.is_open()) { // should my socket class require the user to do this?
std::cout << "failed to open " << filename << '\n';
} else {
// ... use it
}
}
The recommended course of action is to throw an exception on any failure that occurs during construction. Quoting the C++ FAQ:
Q. How can I handle a constructor that fails?
A. Throw an exception.
Constructors don’t have a return type, so it’s not possible to use return codes. 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.
If construction involves RAII then the constructor has the additional responsibility to cleanup any already allocated resources prior to throwing.
Q. How should I handle resources if my constructors may throw exceptions?
A. Every data member inside your object should clean up its own mess.
If a constructor throws an exception, the object’s destructor is not run. If your object has already done something that needs to be undone (such as allocating some memory, opening a file, or locking a semaphore), this “stuff that needs to be undone” must be remembered by a data member inside the object.
As to why std::fstream
constructors do not throw exceptions and use the fallback option by default (put the object into a “zombie” state), that is a combination of history and convenience, better explained in the answers to Why are C++ STL iostreams not “exception friendly”?.