Professional C__ - Marc Gregoire [160]
Alternatively, you could catch the object by reference (without the const):
} catch (exception& e) {
Code snippet from ReadIntegerFile\CatchByNonConstReference.cpp
Also, as you saw in the char* example, you can catch pointers to exceptions, as long as pointers to exceptions are thrown.
It is recommended to catch exceptions by const reference.
Throwing and Catching Multiple Exceptions
Failure to open the file is not the only problem readIntegerFile() could encounter. Reading the data from the file can cause an error if it is formatted incorrectly. Here is an implementation of readIntegerFile() that throws an exception if it cannot either open the file or read the data correctly:
void readIntegerFile(const string& fileName, vector { ifstream istr; int temp; istr.open(fileName.c_str()); if (istr.fail()) { // We failed to open the file: throw an exception. throw exception(); } // Read the integers one by one and add them to the vector. while (istr >> temp) { dest.push_back(temp); } if (istr.eof()) { // We reached the end-of-file. istr.close(); } else { // Some other error. Throw an exception. istr.close(); throw exception(); } } Code snippet from ReadIntegerFile\ThrowingMultipleBasic.cpp Your code in main() does not need to change because it already catches an exception of type exception. However, that exception could now be thrown in two different situations, so you should modify the error message accordingly: try { readIntegerFile(fileName, myInts); } catch (const exception& e) { cerr << "Unable either to open or to read " << fileName << endl; return 1; } Code snippet from ReadIntegerFile\ThrowingMultipleBasic.cpp Alternatively, you could throw two different types of exceptions from readIntegerFile(), so that the caller can tell which error occurred. Here is an implementation of readIntegerFile() that throws an exception object of class invalid_argument if the file cannot be opened and an object of class runtime_error if the integers cannot be read. Both invalid_argument and runtime_error are classes defined in the header file #include void readIntegerFile(const string& fileName, vector { ifstream istr; int temp; istr.open(fileName.c_str()); if (istr.fail()) { // We failed to open the file: throw an exception. throw invalid_argument(""); } // Read the integers one by one and add them to the vector. while (istr >> temp) { dest.push_back(temp); } if (istr.eof()) { // We reached the end-of-file. istr.close(); } else { // Some other error. Throw an exception. istr.close(); throw runtime_error(""); } } Code snippet from ReadIntegerFile\ThrowingTwoTypes.cpp There are no public default constructors for invalid_argument and runtime_error, only string constructors. Now main() can catch both invalid_argument and runtime_error with two catch statements: try { readIntegerFile(fileName, myInts); } catch (const invalid_argument& e) { cerr << "Unable to open file " << fileName << endl; return 1; } catch (const runtime_error& e) { cerr << "Error reading file " << fileName << endl; return 1; } Code snippet from ReadIntegerFile\ThrowingTwoTypes.cpp If an exception is thrown inside the try block, the compiler will match the type of the exception to the proper catch handler. So, if readIntegerFile() is unable to open the file and throws an invalid_argument object, it will be caught by the first catch statement. If readIntegerFile() is unable to read the file properly and throws a runtime_error, then the second catch statement will catch the exception. Matching and const The const-ness specified in the type of the exception you want to catch makes no difference for matching purposes. That is, this line matches any exception of type runtime_error. } catch (const runtime_error& e) { The following line also matches any exception of type runtime_error: } catch (runtime_error& e) { Matching Any Exception You can write a catch line that