Professional C__ - Marc Gregoire [127]
Super* ptr = &mySub;
ptr->overload(7);
The hiding of unimplemented overloaded methods is only skin deep in C++. Objects that are explicitly declared as instances of the subtype will not make the method available, but a simple cast to the superclass will bring it right back.
The using keyword can be employed to save you the trouble of overloading all the versions when you really only want to change one. In the following code, the Sub class definition uses one version of overload() from Super and explicitly overloads the other:
class Super
{
public:
virtual void overload() { cout << "Super's overload()" << endl; }
virtual void overload(int i) {
cout << "Super's overload(int i)" << endl; }
};
class Sub : public Super
{
public:
using Super::overload;
virtual void overload() { cout << "Sub's overload()" << endl; }
};
The using clause has certain risks. Suppose a third overload() method is added to Super, which should have been overridden in Sub. This will now not be detected as an error because due to the using clause, the designer of the Sub class has explicitly said “I am willing to accept all other overloads of this method from the parent class.”
To avoid obscure bugs, you should override all versions of an overloaded method, either explicitly or with the using keyword, but keep the risks of the using clause in mind.
The Superclass Method Is private or protected
There’s absolutely nothing wrong with overriding a private or protected method. Remember that the access specifier for a method determines who is able to call the method. Just because a subclass can’t call its parent’s private methods doesn’t mean it can’t override them. In fact, overriding a private or protected method is a common pattern in object-oriented languages. It allows subclasses to define their own “uniqueness” that is referenced in the superclass.
For example, the following class is part of a car simulator that estimates the number of miles the car can travel based on its gas mileage and amount of fuel left:
class MilesEstimator
{
public:
virtual int getMilesLeft() {
return getMilesPerGallon() * getGallonsLeft();
}
virtual void setGallonsLeft(int inValue) { mGallonsLeft = inValue; }
virtual int getGallonsLeft() { return mGallonsLeft; }
private:
int mGallonsLeft;
virtual int getMilesPerGallon() { return 20; }
};
Code snippet from MilesEstimator\MilesEstimator.h
The getMilesLeft() method performs a calculation based on the results of two of its own methods. The following code uses the MilesEstimator to calculate how many miles can be traveled with 2 gallons of gas:
MilesEstimator myMilesEstimator;
myMilesEstimator.setGallonsLeft(2);
cout << "I can go " << myMilesEstimator.getMilesLeft() << " more miles." << endl;
Code snippet from MilesEstimator\TestMilesEstimator.cpp
The output of this code is as follows:
I can go 40 more miles.
To make the simulator more interesting, you may want to introduce different types of vehicles, perhaps a more efficient car. The existing MilesEstimator assumes that all cars get 20 miles per gallon, but this value is returned from a separate method specifically so that a subclass could override it. Such a subclass is shown here:
class EfficientCarMilesEstimator : public MilesEstimator
{
private:
virtual int getMilesPerGallon() { return 35; }
};
Code snippet from MilesEstimator\EfficientCarMilesEstimator.h
By overriding this one private method, the new class completely changes the behavior of existing, unmodified, public methods. The getMilesLeft() method in the superclass will automatically call the overridden version of the private getMilesPerGallon() method. An example using the new class is as follows:
EfficientCarMilesEstimator myEstimator;
myEstimator.setGallonsLeft(2);
cout << "I can go " << myEstimator.getMilesLeft() << " more miles." << endl;
Code snippet from MilesEstimator\TestMilesEstimator.cpp
This time, the output reflects the overridden functionality:
I can go 70 more miles.
Overriding private and protected methods is a good way to change