The C++Course provides a general introduction to programming in C++. It is based on A.B. Downey's book, How to Think Like a Computer Scientist. Click here for details. |
![]() |
Home ![]() ![]() |
||
![]() ![]() ![]() ![]() ![]() ![]() |
||
Message Class
An an example of inheritance, we are going to take a message class and create a subclass of error messages. That is, we are going to create a new class called ErrorMessage that will have all the instance variables and functions of a Message, plus an additional member variable, errorCode, which will be displayed when the object is outputted. The Message class definition looks like this: class Message{ protected: pstring source; //source of message pstring message; //text in message public: //constructor Message(const pstring& src, const pstring& msg) { source = src; //initialize source message = msg; //initialize message } //convert message to pstring virtual pstring getMessage() const { return source + ": " + message; } };
You probably noticed that there is a const floating in free-space after the getMessage function declaration. When variables are declared const (such as in the Message constructor), it indicates that the function can't modify their values. However, a const after a function declaration means that the function itself is const! Only member functions can use this feature, because what it means is that the function can't modify any member variables of its class. Think of it as if *this is marked const. The virtual indicator at the beginning of getMessage is a very important feature of inheritance. When a function is marked virtual, it allows that function to be redefined in subclasses. We will use this feature to change the behavior of getMessage in the ErrorMessage class. Now here is an example of an ErrorMessage class which extends the functionality of a basic Message: class ErrorMessage : public Message{ protected: pstring errorCode; //error messages have error codes public: //constructor ErrorMessage(const pstring& ec, const pstring& src, const pstring& msg) { errorCode = ec; //initialize error code source = src; //initialize source message = msg; //initialize message } //convert message to pstring virtual pstring getMessage() const { return "ERROR " + errorCode + ": " + source + ": " + message; } }; The class declaration indicates that ErrorMessage inherits from Message. A colon followed by the keyword public is used to identify the parent class. The ErrorMessage class has one additional member variable for an error code, which is added to the string returned from the getMessage function. It would serve to notify a user of the error code associated with whatever message they received. The constructor of ErrorMessage initializes both the original member variables, source and message, and the new errorCode variable. An important thing to note is that the getMessage function has been redefined in ErrorMessage. Now the returned string includes the error code of the message. Suppose we want to overload the << operator to call getMessage in order to display messages. ostream& operator << (ostream& os, const Message& msg) {return os << msg.getMessage(); } This function will take any Message object and display it by calling its getMessage function. Since the ErrorMessage class is inherited from the Message class, what that means is that every ErrorMessage object is also a Message object! This allows you to use displayMessage like this: ErrorMessage error ("1234", "Hard drive", "Out of space");cout << error << endl; The code first creates an ErrorMessage object with three strings for the source, message, and error code. Then the error message is passed to <<. Inside <<, the Message object's getMessage function is called in order to get a string representation of the object for output. The resulting output is: ERROR 1234: Hard drive: Out of spaceEven though the function thinks the object is a Message, and has probably never even heard of the ErrorMessage class, it is still calling a function defined in ErrorMessage. This is all because of the virtual keyword used in the getMessage declaration. All functions that are ever going to be redefined in subclasses must be declared virtual. Otherwise, << would not realize the object is an ErrorMessage and would go ahead and call the getMessage defined in Message instead. As an excercise, remove the virtuals and recompile the program. See if you can predict the output before running it.
|
||
Home ![]() ![]() |