Professional C__ - Marc Gregoire [123]
BingCherry* BingCherryTree::pick()
{
BingCherry* theCherry = new BingCherry();
theCherry->polish();
return theCherry;
}
Code snippet from Cherry\BingCherryTree.h
A good way to figure out whether you can change the return type of an overridden method is to consider whether existing code would still work. In the preceding example, changing the return type was fine because any code that assumed that the pick() method would always return a Cherry* would still compile and work correctly. Because a BingCherry is a Cherry, any methods that were called on the result of CherryTree’s version of pick() could still be called on the result of BingCherryTree’s version of pick().
You could not, for example, change the return type to something completely unrelated, such as void*. The following code will not compile because the compiler thinks that you are trying to override CherryTree::pick(), but cannot distinguish BingCherryTree’s pick() method from CherryTree’s pick() method because return types are not used in method disambiguation:
void* BingCherryTree::pick() // BUG!
{
BingCherry* theCherry = new BingCherry();
theCherry->polish();
return theCherry;
}
This will generate a compiler error, something like 'BingCherryTree::pick': overriding virtual function return type differs and is not covariant from 'CherryTree::pick'.
Changing the Method Parameters
If you use the name of a virtual method from the parent class in the definition of a subclass, but it uses different parameters than the method of that name uses in the parent class, it is not overriding the method of the parent class — it is creating a new method. Returning to the Super and Sub example from earlier in this chapter, you could attempt to override someMethod() in Sub with a new argument list as follows:
class Super
{
public:
Super();
virtual void someMethod();
};
class Sub : public Super
{
public:
Sub();
virtual void someMethod(int i); // Compiles, but doesn't override
virtual void someOtherMethod();
};
The implementation of this method could be as follows:
void Sub::someMethod(int i)
{
cout << "This is Sub's version of someMethod with argument " << i
<< "." << endl;
}
The preceding class definition will compile, but you have not overridden someMethod(). Because the arguments are different, you have created a new method that exists only in Sub. If you want a method called someMethod() that takes an int, and you want it to work only on objects of class Sub, the preceding code is correct.
In fact, the C++ standard says that the original method is now hidden as far as Sub is concerned. The following sample code will not compile because there is no longer a no-argument version of someMethod().
Sub mySub;
mySub.someMethod(); // BUG! Won't compile because original method is hidden.
There is a somewhat obscure technique you can use to have your cake and eat it too. That is, you can use this technique to effectively “override” a method in the subclass with a new prototype but continue to inherit the superclass version. This technique uses the using keyword to explicitly include the superclass definition of the method within the subclass as follows:
class Super
{
public:
Super();
virtual void someMethod();
};
class Sub : public Super
{
public:
Sub();
using Super::someMethod; // Explicitly "inherits" the Super version
virtual void someMethod(int i); // Adds a new version of someMethod
virtual void someOtherMethod();
};
It is rare to find a method in a subclass with the same name as a method in the superclass but using a different parameter list.
The override Keyword
Sometimes, it is possible to accidentally create a new virtual method instead of overriding a method from the superclass. Take the following Super and Sub classes where Sub is properly overriding someMethod():
class Super
{
public:
Super();
virtual void someMethod(double d);
};