Monday, 9 March 2015

Run-time type identification (RTTI)

Run-time type identification (RTTI) with examples:

Using RTTI(Run time type identification) we can find the exact type of the object
The RTTI is useful when the when exception handling was added to C++, When exception handling was added to C++, it required the exact type information about objects. It became an easy next step to build access to that information into the language.

Let us see the simple “Shape” example:

This is an example of a class hierarchy that uses polymorphism. The generic type is the base
Class Shape, and the specific derived types are Point, Line, and Area:
polymorphism example

Let us discuss about the above diagram. We can inherit the properties of the base class to the derived class. Using the polymorphic nature we can extend the program by adding any other type of the class without affecting the existing code. In this example, the virtual function in the Shape interface is drawShape( ), so the intent is for the client programmer to call drawShape( ) through a generic Shape pointer.

drawShape( ) is redefined in all the derived classes, and because it is a virtual function, the proper
behavior will occur even though it is called through a generic Shape pointer.
Thus, you generally create a specific object (Point, Line, or Area), take its address
And cast it to a Shape pointer, and use that anonymous pointer in the rest of the program.  So using up casting is comes into the picture to cast from derived to the base type.

Now we can move into the real topic RTTI.

First we can get the question in our mind, what is RTTI???

Why we need to know the exact type of the object at the runtime, There are some scenarios.
For example, suppose you want to allow your users to change all the shapes of any particular type by turning them Red.
This way, they can find all the Areas on the screen by highlighting them.
You may have seen library member functions with names like isA( ) and typeOf( ). These are vendor-defined RTTI functions. Using these functions, as you go through the list you can say, “If you’re a
Area, turn Red.”

The syntaxes of the RTTI:

RTTI allows programs that use pointers or references to base classes to retrieve the actual derived types of the objects to which these pointers or references refer.
RTTI provided through two operators:
The typeid operator, which returns the actual type of the object referred to by a pointer (or a reference).
The dynamic_cast operator, which safely converts from a pointer (or reference) to a base type to a pointer (or reference) to a derived type.
source code for the class and could change it. Here’s an example that counts shapes using
both the static member approach and dynamic_cast:

shape is the abstract base classes contains drawShape as the pure virtual function, from the shape we can derive different shapes.

Here in this example using the dynamic_cast:
class Shape {

protected:
static int iSCount;  // holds the list of shapes count in the static variable

public:

  /* Constructor of the class used to increment the shapes count */
  Shape ()
    { iSCount++; }

    /* destructor of the class used to decrement the shapes count */
  virtual ~Shape ()
    { iSCount--; }
    /* This is the pure virtual function */
  virtual void drawShape() const = 0;

    /* This function is used to get the GetTotShapes of the shape counts */
  static int GetTotShapes ()
    { return iSCount; }
};
Point class implementation.
int Shape::iSCount = 0;
class Point : public Shape
{
private:
  void operator= (Point&);

protected:
  static int iSCount;
public:

    /* This is the constructor of the class used to increment the shapes count */
  Point ()
    { iSCount++; }
     /* This is the copy constructor of the class used to increment the shapes count */
  Point (const Point&)
    { iSCount++;}
   /* This is the destructor of the class used to decrement the shapes count */
  ~Point ()
    { iSCount--; }

    /* This function is used to draw the point shape */
  void drawShape () const
    { cout << "Point::drawShape()" << endl; }

    /* This function is used to get the total number of points that we have drawn */
  static int GetTotShapes ()
    { return iSCount; }
};

Line Shape class implementation
int Point::iSCount = 0;

class Line : public Shape
{

private:
  void operator= (Line&);

protected:
  static int iSCount;

public:

    /* This is the constructor of the class used to increment the shapes count */
  Line ()
    { iSCount++; }

       /* This is the copy constructor of the class used to increment the shapes count */
  Line (const Line&)
    { iSCount++; }

    /* This is the destructor of the class used to decrement the shapes count */
 ~Line ()
    { iSCount--; }

    /* This function is used to draw the point shape */
  void drawShape () const
    { cout << "Line::drawShape ()" << endl; }

    /* This function is used to get the total number of Line that we have drawn */
  static int GetTotShapes ()
    { return iSCount; }
};

Area Shape implementation
int Line::iSCount = 0;
class Area : public Shape
{
private:
  void operator= (Area&); 

protected:
  static int iSCount;

public:

    /* This is the constructor of the class used to increment the shapes count */
  Area ()
    { iSCount++; }

    /* This is the copy constructor of the class used to increment the shapes count */
  Area (const Area&)
    { iSCount++; }

    /* This is the destructor of the class used to decrement the shapes count */
  ~Area ()
    { iSCount--; }

    /* This function is used to draw the point shape */
  void drawShape () const
    { cout << "Area::drawShape()" << endl; }

   /* This function is used to get the total number of Line that we have drawn */
 static int GetTotShapes ()
   { return iSCount; }
};


Testing the application in the main function:
int Area::iSCount = 0;
int main ()
{
  vector<Shape*> vectShapes;
  srand (time(0)); // Seed random number generator
  const int mod = 12;
  int iPoints = 0, iLines = 0, iAreas = 0, iShapes = 0;

  // Create a random GetTotShapes of each type:
  for (int iInd = 0; iInd< rand () % mod; iInd++)
    { vectShapes.push_back(new Point); }
  for (int iIndex = 0; iIndex < rand () % mod; iIndex++)
    { vectShapes.push_back(new Line); }
  for (int iIn = 0; iIn < rand () % mod; iIn++)
    { vectShapes.push_back (new Area); } 
  for (int iVectIndex = 0; iVectIndex < vectShapes.size (); iVectIndex++) {
    vectShapes[iVectIndex]->drawShape ();
    if (dynamic_cast<Area*> (vectShapes[iVectIndex]))
      { iPoints++; }
    if (dynamic_cast<Line*> (vectShapes[iVectIndex]))
      { iLines++; }
    if (dynamic_cast<Point*> (vectShapes[iVectIndex]))
      { iAreas++; }
    if (dynamic_cast<Shape*> (vectShapes[iVectIndex]))
     { iShapes++; }
  }
  cout << endl << endl << "Points = " << iPoints << endl
                       << "Lines = " << iLines << endl
                       << "Areas = " << iAreas << endl
                       << "vectShapes = " << iShapes << endl << endl
                       << "Area::GetTotShapes() = " << Area::GetTotShapes () << endl
                       << "Line::GetTotShapes() = " << Line::GetTotShapes () << endl
                       << "Point::GetTotShapes() = " << Point::GetTotShapes () << endl
                       << "Shape::GetTotShapes() = " << Shape::GetTotShapes () << endl;
} 

Te output of the above program is
Point::drawShape()
Point::drawShape()
Line::drawShape ()
Area::drawShape()
Area::drawShape()
Area::drawShape()
Area::drawShape()
Area::drawShape()

Points = 5
Lines = 1
Areas = 2
vectShapes = 8

Area::GetTotShapes() = 5
Line::GetTotShapes() = 1
Point::GetTotShapes() = 2
Shape::GetTotShapes() = 8
The dynamic_cast operator is intended to be the most heavily used RTTI component. It doesn't give us what type of object a pointer points to. it can safely assign the address of an object to a pointer of a particular type.
dynamic_cast involves a run-time type check. If the object bound to the pointer is not an object of the target type, it fails and the value is 0. If it's a reference type when it fails, then an exception of type bad_cast is thrown .
The typeid:
typeid operator allows us to determine whether two objects are the same type.
 Let us look into one example using typeid:
For tis we have to include
#include <typeinfo>
#include "stdafx.h"
#include <iostream>
#include <typeinfo>
using namespace std;
class Base {
private:
  int id;
public:
  void BaseFun ()
    { cout << "I am in the base function" << endl;  }
};
class Derived : public Base {

public:
 void DerivedFun ()
   { cout << "I am in the derived function" << endl; }
};
int main ()
{
  Base ocBase;
  Derived ocDerived;
  Base *pcBase1 = &ocBase;
  Base *pcBase2 = &ocDerived; 
  if(typeid (Derived) == typeid (ocBase)) {
    Derived *pcDerived = (Derived*)&ocBase;
    pcDerived->DerivedFun ();
  }
  if (typeid (Derived) == typeid (ocDerived)) {
    Derived *pcDerived = (Derived *)&ocDerived;
    pcDerived->DerivedFun ();
  }
  pcBase1->BaseFun ();
  pcBase2->BaseFun ();
  getchar ();
  return 0;
} 

The output of the above program is
I am in the derived function

I am in the base function

I am in the base function

No comments: