|

Overriding functions - Overriding functions involves changing the implementation of a base class function in a
derived class. When you make an object of the derived class, the correct function is called,
specific to the type
of the child object rather than the parent object
it inherits from. It is when a derived class creates a function
with the same return type and signature as a member in the base class, but with a new implementation. It is
"overriding" that method. When you override a function,
it must agree in return type and signature with the
function in the base
class. Recall the definition of a function's signature:
|
"If
the automobile had followed the same development
cycle as the computer, a Rolls-Royce would today
cost $100, get a million miles per gallon, and
explode once a year, killing everyone inside."
- Robert X. Cringely, Computerworld |
Function
Signature - A function signature is a function prototype other than the return type - the
name, the parameter list, and the keyword "const" if used. The
signature of a function is its name and the # and type of its
parameters. The signature does not include the return type.
Below is an example of the Cat class overriding the speak() method in Mammal:
//Overriding a base class method in a derived class
#include <iostream.h>
enum BREED {
RussianBlue, Persian, Siamese, Tabby, TortoiseShell,
Himilayan };
//--------------------------------------------------------------------------------------------------------------------
class Mammal
{
public:
//constructors
Mammal() { cout << "Mammal constructor...\n"; }
~Mammal() { cout << "Mammal destructor...\n"; }
//Other methods
void Speak()const { cout << "Mammal sound!\n"; }
void Sleep()const { cout << "Sleeping.\n"; }
protected:
int itsAge;
int itsWeight;
};
//--------------------------------------------------------------------------------------------------------------------
class Cat
: public Mammal
{
public:
//Constructors
Cat(){ cout << "Cat constructor...\n"; }
~Cat(){ cout << "Cat destructor...\n"; }
//Other methods
void SwishTail() { cout << "Cat is
angry...\n"; }
void ChaseMice() { cout << "Chasing
mice...\n"; }
void
Speak()const { cout << "Meow!\n"; }
private:
BREED itsBreed;
};
//--------------------------------------------------------------------------------------------------------------------
int main()
{
Mammal SmallAnimal;
Cat Felix;
SmallAnimal.Speak();
Felix.Speak();
return 0;
} |
Output:
Mammal constructor . . .
Mammal constructor . . .
Cat constructor . . .
Mammal sound!
|
Meow!
Cat destructor . . .
Mammal destructor . . .
Mammal destructor . . . |
In this example, the Cat class overrides the speak() method
defined in its base class, Mammal, causing Cat objects to say "Meow!"
instead of the base class's "Mammal sound". In this way, the
Cat class Speak() method is said to be "overriding" the Mammal class
Speak() method. In this way, we can give methods that perform
similar functions the same name and they will "do the right thing",
based on which derived class uses that method. At this point, some
people confuse overloading with overriding, so let's review:
Overloading vs. Overriding
- When you overload a method, you create more than 1 method with the same name, but
DIFFERENT signatures.
- When you override a method, you create a method in a derived class with the same name as a method in the
base class, and with the SAME signature (argument
and return type).
Hiding the base class method -
Hiding a base class method through overriding can cause unexpected problems.
If the Mammal class has a method called Move() that is overloaded, and
the Cat class overrides that method, the Cat method will hide all the Mammal methods with that name. If Mammal
class overloads Move() as 3 methods - 1=takes no parameters, 2=takes an int, 3=takes int and
a direction, and the Cat class overrides only the Move() method which takes no parameters, it will not be easy to access the other 2 methods using a
Cat object. Example:
//Hiding methods
#include <iostream.h>
enum BREED {
RussianBlue, Persian, Siamese, Tabby, TortoiseShell,
Himilayan };
//--------------------------------------------------------------------------------------------------------------------
class Mammal
{
public:
//Constructor
and destructor
Mammal() { cout << "Mammal constructor...\n"; }
~Mammal() { cout << "Mammal destructor...\n"; }
//Overloaded
Move() methods
void Move()
const { cout << "Mammal move one step\n"; }
void Move(int distance)
const
{ cout << "Mammal move " << distance <<" _steps.\n"; }
void Move(int
distance, char Direction)
const
{ cout << "Mammal moving to the " << Direction <<
".";}
protected:
int itsAge;
int itsWeight;
};
//--------------------------------------------------------------------------------------------------------------------
class Cat :
public Mammal
{
public:
//Constructor and
Destructor
Cat(){ cout << "Cat constructor...\n"; }
~Cat(){ cout << "Cat destructor...\n"; }
//Other methods
void SwishTail() { cout << "Cat is
angry...\n"; }
void ChaseMice() { cout << "Chasing
mice...\n"; }
void Speak()const { cout << "Meow!\n"; }
//You may receive a compiler warning that you are hiding a function!
void Move()
const { cout << "Cat move 5 steps.\n"; }
private:
BREED itsBreed;
};
//--------------------------------------------------------------------------------------------------------------------
int main()
{
Mammal SmallAnimal;
Cat Morris;
//Call Mammal's Move() methods
SmallAnimal.Move();
SmallAnimal.Move(2);
//Call the Cat class Move() method that overrides
the Mammal class Move() with no parameters
Morris.Move();
//The line below will produce an error since nothing
overrides the Mammal class methods that take
parameters
//Morris.Move(10);
return 0;
} |
Output:
Mammal move one step
Mammal move two steps.
Cat move 5 steps.
In the example above, the Mammal class declares three overloaded Move() methods.
In the derived Cat class, the version of Move() with no parameters from
the base class, Mammal, is overridden with a method specific to Cat
objects rather than Mammals. The next to the last line,
Morris.Move(10);
has to be commented out, it will cause a compile-time error. The
Cat class could have called the Move(int) method if it had not overridden the
default Mammal class version of Move() without parameters.
However, now that the Cat class has done so, it must override both
Move() methods if it wants to use both. Remember: if you supply any constructor, the compiler will no longer provide the default constructor.
Important
Note: It is a common mistake to hide a base class method when you intend to override it.
This is usually accomplished by forgetting to include the keyword "const".
The keyword "const" is
a part of the signature, and leaving it off changes the signature and hides the method rather than overriding it.
Remember that methods being overridden must possess the SAME signature.
Calling the base method - By explicitly calling the base method,
you can work around this. If you overload the base method, you can still call it by fully qualifying the name of the method. You do this by writing the base name followed by 2 colons and then the method name. Example:
DerivedClassObject.Mammal::Move();
We could rewrite the last few lines
of the program above so that is would compile correctly. Example:
// . . . (use the same code as in
the example above)
int main()
{
Mammal SmallAnimal;
Cat Morris;
//Call Mammal's Move() methods
SmallAnimal.Move();
SmallAnimal.Move(2);
//Call the Cat class Move() method that overrides
the Mammal class Move() with no parameters
Morris.Move();
Morris.Mammal::Move(10);
return 0;
} |
Output:
Mammal move 2 steps.
Mammal move 10 steps.
...
Note: The programmer wanted to invoke Move(int) on the Cat object, but had a problem.
The Cat class overrides the Move() method with no parameters, but it does not overload it.
Neither does the Cat class provide a version of Move() that takes an int,
thus overriding the Mammal class method that takes an int parameter. This is solved by the
explicit call to the base class Move(int) method on the last few
lines.
Things to Remember:
1)
DO - extend the functionality of classes by deriving methods and data
members from the base class.
2)
DO - change behavior of functions in derived classes by overriding their base class methods
as appropriate.
3)
DON'T - hide a base class function by changing the function signature
without realizing it. A common mistake is removing "const".
©2004 C. Germany
|