After deriving a class from another one, we have seen how
you can "re-write" a property or method of the parent in the child
class. When using a property or method in a child class, by default, if you
call a property or method that is available in both the child and the parent
class, the compiler would directly access the version in the child class.
Consider the following example:
using namespace System;
public ref class CSquare
{
public:
virtual String ^ Description();
virtual void Display();
};
String ^ CSquare::Description()
{
return L"A Square is a geometric shape with 4 equal sides";
}
void CSquare::Display()
{
Console::WriteLine(L"Geomtric Shapes");
Console::WriteLine(L"Name: Square");
Console::WriteLine(L"Description {0}", Description());
}
public ref class CCube : public CSquare
{
public:
virtual String ^ Description() override;
virtual void Display() override;
};
String ^ CCube::Description()
{
return L"A cube is 3-dimensional box with 6 squares";
}
void CCube::Display()
{
Console::WriteLine(L"Geomtric Shapes");
Console::WriteLine(L"Name: Cube");
Console::WriteLine(L"Description {0}", Description());
}
int main()
{
CCube ^ cube = gcnew CCube;
cube->Display();
Console::WriteLine();
return 0;
}
This would produce:
Geomtric Shapes
Name: Cube
Description: A cube is 3-dimensional box with 6 squares
Press any key to continue . . .
In the result of this program, the application defines a
cube and includes the word square without defining what a square is. Based on the rules
of overridden members that we have seen so far, you cannot directly access the
Description() method of the CSquare class from a method of the CCube class. The
solution is to "qualify" the parent method when calling it. This
qualification is performed using the scope resolution operator :: by accessing the method in the
parent class as if it were a static method. This can be done as follows:
void CCube::Display()
{
Console::WriteLine(L"Geometric Shapes");
Console::WriteLine(L"Name: Cube");
Console::WriteLine(L"Description {0}", CSquare::Description());
Console::WriteLine(L" {0}", Description());
}
int main()
{
CCube ^ cube = gcnew CCube;
cube->Display();
Console::WriteLine();
return 0;
}
This would produce:
Geomtric Shapes
Name: Cube
Description A Square is a geometric shape with 4 equal sides
A cube is 3-dimensional box with 6 squares
Press any key to continue . . .
In the same way, from the child class, you can access the
members of the parent class by qualifying them but calling them as you would a
static member.
Overloading a Method of a Parent Class
|
|
Consider the following virtual class:
Header File: Square.h |
#pragma once
#include <string>
using namespace std;
class CSquare
{
protected:
double _side;
public:
CSquare(void);
CSquare(double side);
virtual ~CSquare(void);
virtual string Description();
virtual void Display();
double getSide() const { return _side; }
void setSide(const double sd) { _side = (sd <= 0) ? 0 : sd; }
virtual double Area()
{
return _side * _side;
}
virtual double Perimeter()
{
return _side * 4;
}
};
|
Source File: Square.cpp |
#include <iostream>
#include <string>
#include "Square.h"
using namespace std;
CSquare::CSquare(void) : _side(0.00)
{
}
CSquare::CSquare(double side)
: _side((side <= 0.00) ? 0.00 : side)
{
}
CSquare::~CSquare(void)
{
}
string CSquare::Description()
{
return "A Square is a geometric shape with 4 equal sides";
}
void CSquare::Display()
{
cout << "Geometric Shapes" << endl;
cout << "Name: Cube" << endl;
cout << "Description " << Description() << endl;
cout << "Side: " << getSide() << endl;
cout << "Perimeter: " << Perimeter() << endl;
cout << "Area: " << Area() << endl;
}
|
This class can be tested as follows:
Source File: Exercise.cpp |
#include <iostream>
#include <string>
#include "Square.h"
using namespace std;
int main()
{
CSquare * care = new CSquare;
care->setSide(24.85);
care->Display();
cout << endl;
return 0;
}
|
This would produce:
Geometric Shapes
Name: Cube
Description A square is a geometric shape with 4 equal sides
Side: 24.85
Perimeter: 99.4
Area: 617.5225
Press any key to continue . . .
Notice that, in the main() function, we called the
Display() method of the CSquare class as we have been doing so far and we had to
qualify the name of the method to clearly show what class it belongs to. In the
same way, if you derive one class from another, when in the body of the derived
class, if you want to access a member of the parent class, we saw that you could
qualify the method.
In a derived class, if you want to re-implement a method of
the parent class, instead of formally overriding it, you can overload it. The
difference is, and you should be aware of it, that when overriding, you are
redefining a method with the exact same signature (same name and same argument(s),
if any). When overloading a method, remember that they different versions must
have the same name but different argument(s). Based on this, if you overload a
method in a child class, give it the same name but change something about the
argument(s), either a different type of argument or a different number of
arguments. Here is an example:
Header File: Cube.h |
#pragma once
#include "square.h"
class CCube : public CSquare
{
public:
CCube(void);
~CCube(void);
virtual string Description(const int cube);
virtual void Display();
virtual double Area();
virtual double Volume();
};
|
Notice that, the Description() method that is also available
in the parent class without an argument, this time takes a constant integer.
Also remember that you don't even have to use the argument; it is only used to
distinguish it from the other one. When implementing the new version, you can do
whatever you want with it and call it as you see fit.
Consider the following class:
Header File: Cube.h |
#pragma once
#include "square.h"
class CCube : public CSquare
{
public:
CCube(void);
~CCube(void);
virtual string Description(const int cube);
virtual void Display();
virtual double Area(const int cube);
virtual double Volume();
};
|
Source File: Cube.cpp |
#include <iostream>
#include <string>
#include "Cube.h"
using namespace std;
CCube::CCube(void)
{
}
CCube::~CCube(void)
{
}
string CCube::Description(const int sqr)
{
return "A cube is 3-dimensional box with 6 squares";
}
double CCube::Area(const int cube)
{
return _side * 6;
}
double CCube::Volume()
{
return _side * _side * _side;
}
void CCube::Display()
{
cout << "Geomtric Shapes" << endl;
cout << "Name: Cube" << endl;
cout << "Description " << Description(0) << endl;
cout << " " << Description() << endl;
cout << "Side: " << getSide() << endl;
cout << "Side Area: " << Area() << endl;
cout << "Total Area: " << Area(0) << endl;
}
|
Source File. Exercise.cpp |
#include "Cube.h"
int main()
{
CCube * cube = new CCube;
cube->setSide(24.85);
cube->Display();
return 0;
}
|
This program would not work because, in the
CCube::Display()
method, the compiler cannot locate the Description() method. When calling an
overloaded method of a parent class from a derived class, in the derived class,
as opposed to qualifying the name of the method as we learned in the previous
section, you can create a synonym of of the method. To do this, in the body of
the derived class, type the using keyword, followed by the name of the parent class,
followed by the ::operator, followed by the name of the method that you had
overridden. Here are two examples:
Header File: Cube.h |
#pragma once
#include "square.h"
class CCube : public CSquare
{
public:
CCube(void);
~CCube(void);
virtual string Description(const int cube);
virtual void Display();
using CSquare::Description;
using CSquare::Area;
virtual double Area(const int cube);
virtual double Volume();
};
|
This time, when calling the overridden method(s), the
compiler would know which one you are referring to. The program would produce:
Geomtric Shapes
Name: Cube
Description A cube is 3-dimensional box with 6 squares
A Square is a geometric shape with 4 equal sides
Side: 24.85
Side Area: 617.523
Total Area: 149.1
Press any key to continue . . .
|