Professional C__ - Marc Gregoire [114]
class WeatherPrediction
{
public:
virtual std::string getTemperature() const;
// Omitted for brevity
};
Code snippet from WeatherPrediction\WeatherPrediction.h
You can override this method in the MyWeatherPrediction class as follows:
class MyWeatherPrediction : public WeatherPrediction
{
public:
virtual std::string getTemperature() const;
// Omitted for brevity
};
Code snippet from WeatherPrediction\MyWeatherPrediction.h
Suppose the subclass wants to add °F to the string by first calling the superclass getTemperature() method and then adding °F to the string. You might want to try to write this as follows:
string MyWeatherPrediction::getTemperature() const
{
return getTemperature() + "°F"; // BUG
}
However, this will not work because, under the rules of name resolution for C++, it first resolves against the local scope, then the class scope, and as a consequence will end up calling MyWeatherPrediction::getTemperature(). This will result in an infinite recursion until you run out of stack space (some compilers detect this error and report it at compile time).
To make this work, you need to use the scope resolution operator as follows:
string MyWeatherPrediction::getTemperature() const
{
return WeatherPrediction::getTemperature() + "°F";
}
Code snippet from WeatherPrediction\MyWeatherPrediction.cpp
Microsoft Visual C++ supports the __super keyword (with two underscores). This allows you to write the following:
return __super::getTemperature() + "°F";
Calling the parent version of the current method is a commonly used pattern in C++. If you have a chain of subclasses, each might want to perform the operation already defined by the superclass but add their own additional functionality as well.
As another example, imagine a class hierarchy of types of books. A diagram showing such a hierarchy is shown in Figure 8-4.
FIGURE 8-4
Since each lower class in the hierarchy further specifies the type of book, a method that gets the description of a book really needs to take all levels of the hierarchy into consideration. This can be accomplished by chaining to the parent method. The following code illustrates this pattern. The code also defines a virtual getHeight() method, discussed further after the example.
class Book
{
public:
virtual string getDescription() { return "Book"; }
virtual int getHeight() { return 120; }
};
class Paperback : public Book
{
public:
virtual string getDescription() {
return "Paperback " + Book::getDescription();
}
};
class Romance : public Paperback
{
public:
virtual string getDescription() {
return "Romance " + Paperback::getDescription();
}
virtual int getHeight() { return Paperback::getHeight() / 2; }
};
class Technical : public Book
{
public:
virtual string getDescription() {
return "Technical " + Book::getDescription();
}
};
int main()
{
Romance novel;
Book book;
cout << novel.getDescription() << endl; // Outputs "Romance Paperback Book"
cout << book.getDescription() << endl; // Outputs "Book"
cout << novel.getHeight() << endl; // outputs "60"
cout << book.getHeight() << endl; // outputs "120"
return 0;
}
Code snippet from Book\Book.cpp
The Book class defines a virtual getHeight() method, returning 120. Only the Romance class overrides this by calling getHeight() on its parent class (Paperback) and dividing the result by two as follows:
virtual int getHeight() { return Paperback::getHeight() / 2; }
However, Paperback does not override getHeight(), so C++ will walk up the class hierarchy to find a class that implements getHeight(). In the preceding example, Paperback::getHeight() will resolve to Book::getHeight().
Casting Up and Down
As you have already seen, an object can be cast or assigned to its parent class. If the