Abstract Class & Virtual function - Real Implementation?

W

WittyGuy

Hi all,
Though I know the concepts of both abstract class & virtual
function (like derived class pointer pointing to base class...then
calling the function with the pointer...), what is the real
implementation usage of these concepts? Where these concepts will be
used. Please provide some illustration (real-time), so that it can be
easily comprehended.

Thanks,
wittyGuy
 
C

codigo

WittyGuy said:
Hi all,
Though I know the concepts of both abstract class & virtual
function (like derived class pointer pointing to base class...then
calling the function with the pointer...), what is the real
implementation usage of these concepts? Where these concepts will be
used. Please provide some illustration (real-time), so that it can be
easily comprehended.

Thanks,
wittyGuy

Take a look around you, find me an object which you couldn't classify. In
reality, the whole world around you is classified in some way. Life and
genes, elements and materials, businesses and products, date and time,
energy and work, etc.

The basic idea is that you can classify (no pun intended), contain, upcast,
or provide easy code-expansion for a new object based on its provided
derivative(s).

Common example
A Shape class can be an abstract class to derive a Triangle class, a
Rectangle class and a Circle class. A pure-virtual getArea() member function
in the abstract Shape class requires you to define the getArea() member
function in each of its derivatives. The Circle and the Rectangle, for
example, don't rely on the same getArea() member function to calculate its
area.

Note that this makes OO sense since:
A Triangle is_a Shape
A Rectangle is_a Shape
A Circle is_a Shape

Containers
The benefit is now you can use a container that holds any Shape (an array of
Shapes, a vector, a list, stack, queue, etc). The container needs not track
which shape element is a circle, a triangle or a rectangle. The Shape
container needs only satisfy that each element is a pointer to an existing
Shape derivative.

Shapes* shapes[4]; // a container of pointers to Shape objects

shapes[0] = new Rectangle(10, 10); // (width, length)
shapes[1] = new Circle(5); // (radius)
shapes[2] = new Triangle(7, 6) // (width, length)
shapes[3] = new Circle(22); // (radius)

The element at shapes[0] knows whether its a Circle, Rectangle or Triangle.
The same goes with every other element. The container doesn't care whether
Array[2] is a Triangle, a Rectangle or a Circle. So when you write:

int n = 0;
while ( n < 4 )
{
Array[n++]->getArea();
}

delete [] shapes;

You are calling the appropriate getArea() behaviour for each Shape-type
element in the container (using a vtable). This would not be possible
without the abstract Shape class (a container of what?). Neither would this
be possible without a pure-virtual getArea() member function in Shape (we
need to guarentee that all Shapes have such a member function available).

This ability to contain any derivative-type can be applied to Animals,
Vehicles, Employees, Airplanes, Planets or any other base class you can
think of. Note that the hierarchy need not be so shallow. A Car is a
derivative of Vehicle, a Sportscar is a derivative of Car, hence a Sportscar
can also be an element in a container of Vehicles.

Whats the benefit? All such Vehicles have an engine, a steering wheel and a
gas tank. There is no need to repeatedly insert an engine in all the derived
classes. A Vehicle base class can be composed of an engine and each derived
class needs only invoke the appropriate engine ctor to equip itself with a
particular engine. The startEngine() member function in Vehicle can now
start the engine in any Vehicle. This can save an enormous amount of code
when you consider the variety of motorized vehicle-types out there.

Should a client programmer choose to employ your classes (expand Shape to
provide a pentagon or octagon - expand Vehicle to provide a FireTruck), his
job has been made much easier for both you, the creator, and the client.
You've used a common abstract base class and pure-virtual member functions
which dictates and guarentees the implementation of the new derived entity
in the program.

A quick scan by the client of your base class permits him or her to draw up
the derived FireTruck. The client can add the water pump, the ladder, valves
and whatever he likes or needs. Yet that FireTruck can still be held in any
Vehicle container (highway, FireHouse, bridge, Ferry, Tunnel, etc) and the
original startEngine() member function still works.

The same principles of simplified code reusability can be applied in
virtually any appropriate classification hierarchy.
 
W

WittyGuy

Hi codigo,
That is a perfect illustration which cleared most of my
doubt regarding abstract class usage. But one concept which still
remains unclear is why is it needed that virtual function (not pure
virtual), to get the base class member function thru derived class
pointer of the object invoked instead using pointer of the base class
object straight-away for invocation?

thanx,
wittyGuy
 
K

Karl Heinz Buchegger

WittyGuy said:
Hi codigo,
That is a perfect illustration which cleared most of my
doubt regarding abstract class usage. But one concept which still
remains unclear is why is it needed that virtual function (not pure
virtual), to get the base class member function thru derived class
pointer of the object invoked instead using pointer of the base class
object straight-away for invocation?

???
Your english is not very clear. At least I cannot figure out
what you are asking about.
The function *is* invoked through a base class pointer. In
codigo's example (when all the syntactic errors are fixed)
all the pointers are base class type pointers.

Here is the compilable, working example again. Note that in
main() the for loop deals with Shape pointers only. Yet the
correct function is invoked.

#include <iostream>

class Shape
{
public:
virtual ~Shape() {}; // needs to be virtual since we are going
// to destroy in a polymorphic way

virtual double GetArea() { return 0.0; } // Could be a pure function if wanted
// since this function should never be called.
// But just in case :)

virtual const char* Description() { return "Shape"; }
};

class Rectangle : public Shape
{
public:
Rectangle( double Width, double Height )
: m_Width( Width ), m_Height( Height )
{}

virtual double GetArea() { return m_Width * m_Height; }
virtual const char* Description() { return "Rectangle"; }

protected:
double m_Width;
double m_Height;
};

class Circle : public Shape
{
public:
Circle( double Radius )
: m_Radius( Radius )
{}

virtual double GetArea() { return m_Radius * m_Radius * 3.1415926; }
virtual const char* Description() { return "Circle"; }

protected:
double m_Radius;
};

int main()
{
Shape* Shapes[2];

Shapes[0] = new Rectangle( 3.0, 5.0 );
Shapes[1] = new Circle( 4.0 );

for( int i = 0; i < 2; ++i ) {
std::cout << i << ": I am a "
<< Shapes->Description()
<< " and my area is "
<< Shapes->GetArea()
<< "\n";
}

delete Shapes[0];
delete Shapes[1];
}
 
C

codigo

WittyGuy said:
Hi codigo,
That is a perfect illustration which cleared most of my
doubt regarding abstract class usage. But one concept which still
remains unclear is why is it needed that virtual function (not pure
virtual), to get the base class member function thru derived class
pointer of the object invoked instead using pointer of the base class
object straight-away for invocation?

thanx,
wittyGuy

You could have used a pure virtual instead of a virtual function. In many
cases this makes sense. A pure virtual getArea() member function requires
that any Shape derivative implement getArea(). It also prevents
instantiation of an underived Shape object.

You could also use a virtual getArea() in the Shape class and protect the
ctor as an alternative (to prevent the instantiation of an underived Shape
object).

The difference between using either a pure-virtual or virtual is that with a
pure-virtual member function, you can't directly call the base's
pure-virtual getArea() and the Shape class is abstract without having to
protect its ctor. However, the vtable mechanism doesn't prevent you from
calling Shape::getArea() from a Circle or Triangle. The language has the
flexibility to meet your needs one way or another.

Lets store the area in an abstract Shape class. Lets calculate the area in
the Shape ctor invocations (in the derived initialization lists).

#include <iostream>
using std::cout;

class Shape
{
const double m_area;
public:
Shape(double a) : m_area(a) { }
virtual ~Shape() {};
virtual double getArea() const = 0
{
return m_area;
}
}; // class Shape

class Circle : public Shape
{
double m_radius;
public:
Circle(double r)
: m_radius(r),
Shape(r * r * 3.1416) { }
~Circle() { }
double getArea() const
{
cout << "Circle's area is ";
return Shape::getArea();
}
}; // class Circle

class Rectangle : public Shape
{
double m_width;
double m_height;
public:
Rectangle(double w, double h)
: m_width(w),
m_height(h),
Shape(w * h) { }
~Rectangle() { }
double getArea() const
{
cout << "Rectangle's area is ";
return Shape::getArea();
}
}; // class Rectangle

class Triangle : public Shape
{
double m_width;
double m_height;
public:
Triangle(double w, double h)
: m_width(w),
m_height(h),
Shape((w * h)/2) { }
~Triangle() { }
double getArea() const
{
cout << "Triangle's area is ";
return Shape::getArea();
}
}; // class Triangle

int main()
{
Shape* shapes[3];

shapes[0] = new Circle(2.5);
shapes[1] = new Rectangle(5.0, 2.0);
shapes[2] = new Triangle(5.0, 2.0);

for( int i = 0; i < 3; ++i )
{
cout << "shapes[" << i << "]: ";
cout << shapes->getArea();
cout << std::endl;
}

delete shapes[0];
delete shapes[1];
delete shapes[2];

return 0;
} // main()

******
output:

shapes[0]: Circle's area is 19.635
shapes[1]: Rectangle's area is 10
shapes[2]: Triangle's area is 5
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,667
Latest member
DaniloB294

Latest Threads

Top