Online Book Reader

Home Category

Professional C__ - Marc Gregoire [166]

By Root 1076 0
ExceptionsAndPolymorphism\WritingExceptions.cpp

As a good programming citizen, you should make FileError a part of the standard exception hierarchy. It seems appropriate to integrate it as a child of runtime_error. When you write a subclass of runtime_error (or any other exception in the standard hierarchy), you need to override the what() method, which has the prototype shown and is supposed to return a const char* string that is valid until the object is destroyed. In the case of FileError, this string comes from the mMsg data member, which is set to "" in the constructor. Subclasses of FileError must set this mMsg string to something different if they want a different message.

The generic FileError class also contains a filename and an accessor for that filename.

The first exceptional situation in readIntegerFile() occurs if the file cannot be opened. Thus, you might want to write a FileOpenError subclass of FileError:

class FileOpenError : public FileError

{

public:

FileOpenError(const string& fileNameIn);

};

FileOpenError::FileOpenError(const string& fileNameIn) : FileError(fileNameIn)

{

mMsg = "Unable to open " + fileNameIn;

}

Code snippet from ExceptionsAndPolymorphism\WritingExceptions.cpp

The FileOpenError changes the mMsg string to represent the file-opening error.

The second exceptional situation in readIntegerFile() occurs if the file cannot be read properly. It might be useful for this exception to contain the line number of the error in the file, as well as the filename and the error message string returned from what(). Here is a FileReadError subclass of FileError:

class FileReadError : public FileError

{

public:

FileReadError(const string& fileNameIn, int lineNumIn);

int getLineNum() { return mLineNum; }

protected:

int mLineNum;

};

FileReadError::FileReadError(const string& fileNameIn, int lineNumIn)

: FileError(fileNameIn), mLineNum(lineNumIn)

{

ostringstream ostr;

ostr << "Error reading " << fileNameIn << " at line " << lineNumIn;

mMsg = ostr.str();

}

Code snippet from ExceptionsAndPolymorphism\WritingExceptions.cpp

Of course, in order to set the line number properly, you need to modify your readIntegerFile() function to track the number of lines read instead of just reading integers directly. Here is a new readIntegerFile() function that uses the new exceptions:

void readIntegerFile(const string& fileName, vector& dest)

throw(FileOpenError, FileReadError)

{

ifstream istr;

int temp;

string line;

int lineNumber = 0;

istr.open(fileName.c_str());

if (istr.fail()) {

// We failed to open the file: throw an exception.

throw FileOpenError(fileName);

}

while (!istr.eof()) {

// Read one line from the file.

getline(istr, line);

lineNumber++;

// Create a string stream out of the line.

istringstream lineStream(line);

// Read the integers one by one and add them to the vector.

while (lineStream >> temp) {

dest.push_back(temp);

}

if (!lineStream.eof()) {

// Some other error. Close the file and throw an exception.

istr.close();

throw FileReadError(fileName, lineNumber);

}

}

istr.close();

}

Code snippet from ExceptionsAndPolymorphism\WritingExceptions.cpp

Now, code that calls readIntegerFile() can use polymorphism to catch exceptions of type FileError like this:

try {

readIntegerFile(fileName, myInts);

} catch (const FileError& e) {

cerr << e.what() << endl;

return 1;

}

Code snippet from ExceptionsAndPolymorphism\WritingExceptions.cpp

There is one trick to writing classes whose objects will be used as exceptions. When a piece of code throws an exception, the object or value thrown is copied. That is, a new object is constructed from the old object by using the copy constructor. It must be copied because the original could go out of scope (and be destroyed and have its memory reclaimed) before the exception is caught, higher up in the stack. Thus, if you write a class whose objects will be thrown as exceptions, you must make those objects copyable. This means that if you have dynamically allocated memory, you must write a destructor, copy

Return Main Page Previous Page Next Page

®Online Book Reader