·       Polymorphism – refers to the ability to associate many meanings to one function name by means of virtual functions and late binding.  Thus, polymorphism, virtual functions, and late binding are all the same topic.

o      The ability to assign an address of an object of a derived class to a pointer to a base class is the essence of polymorphism.

o      Virtual Functions – A virtual function is should be used when you do not know how the function is to be implemented.  The compiler will wait until the function is used in a program and then get the implementation from the object instance.  Virtual functions are the way that C++ provides late binding.

§       Late Binding – When a function call is resolved at run time

§       Early Binding – When a function call is resolved at compile time.

o      How Virtual Member Functions Work – Suppose we have a Mammal class.  When an object derived from the Mammal class (e.g., a Dog object) is created, the constructor for the Mammal class is called first and then the constructor for the Dog class is called.  The memory for the Mammal part of the object is contiguous in memory with the Dog part.  The Dog object would look as follows:

 

When a virtual function is created in an object, the object must keep track of that function.  Compilers build a virtual function table (v-table) to do just that.  A v-table is kept for each type, and each object of that type keeps a virtual table pointer (v-pointer), which points to that table.  Each object’s v-pointer points to the v-table that, in turn, has a pointer to each of the virtual member functions.  Thus, when the Mammal part of the Dog is created, the v-pointer is initialized to point to the v-table for the Mammal class.

When the Dog constructor is called and the dog part of the object is added, the v-pointer is adjusted to point to the virtual function overrides (if any) in the Dog object.  If the Speak() method is overridden in the Dog class, then the pointer is adjusted as shown below:

When a pointer to Mammal is used, the v-pointer continues to point to the correct function, depending on the real type of the object.  Thus, when Speak() is invoked, the correct function is invoked.  If Speak() is virtual and overridden, the implementation from Dog is invoked.  If Speak() is not virtual or is virtual and not overridden, then the implementation from Mammal is invoked.

 

// Example

#include <iostream>

using namespace std;

 

class Mammal

{

public:

      Mammal() : _itsAge(1) { cout << “Mammal Constructor.\n”; }

      virtual ~Mammal() { cout << “Mammal Destructor.\n”; }

 

      void Move() const

      {

            cout << “Mammal moves one step forward.\n”;

      }

 

      virtual void Speak() const

      {

            cout << “Mammal Speak!\n”;

      }

 

protected:

      int _itsAge;

};

 

class Dog : public Mammal

{

public:

      Dog() { cout << “Dog Constructor.\n”; }

      virtual ~Dog() { cout << “Dog Destructor.\n”; }

 

      void WagTail() const

      {

            cout << “Wagging Tail.\n”;

      }

 

      void Move() const

      {

            cout << “Dog moves 4 steps.\n”;

      }

 

 

      virtual void Speak() const

      {

            cout << “Bark!\n”;

      }

 

 

};

 

int main()

{

      cout << “\nOutput using a Mammal Pointer\n”;

 

      Mammal *pDog = new Dog;

     

      /*

       * Move() is not a virtual function.  Therefore, the Move()

       * function that was defined for Mammal is invoked.

       */

      pDog->Move();

 

      /*

       * Speak() is a virtual function.  Therefore, the v-table

       * will point to the overridden Speak() function that was defined

       * for Dog.

       */

      pDog->Speak();

 

       /*

        * Below produces a compiler error because ‘WagTail’ is not a

        * member of ‘Mammal’.  This is known as the slicing problem.  This

 * can be avoided by casting the pointer.

        */

      // pDog->WagTail();

 

       // The following two lines of code are legal casts of the pointer.

      (static_cast<Dog *>(pDog))->WagTail();

      ((Dog *)pDog)->WagTail();

 

      delete pDog;

 

      cout << “\nOutput using a Dog Pointer\n”;

 

      Dog *pDog2 = new Dog;

 

      pDog2->Move();

      pDog2->Speak();

      pDog2->WagTail();

 

      delete pDog2;

 

      return 0;

}

 

 

 

 

 

// Output

Output using a Mammal Pointer

Mammal Constructor.

Dog Constructor.

Mammal moves one step forward.

Bark!

Wagging Tail.

Wagging Tail.

Dog Destructor.

Mammal Destructor.

 

Output using a Dog Pointer

Mammal Constructor.

Dog Constructor.

Dog moves 4 steps.

Bark!

Wagging Tail.

Dog Destructor.

Mammal Destructor.

Press any key to continue

 

o      Some Tidbits about Virtual Functions

§       Virtual Functions only operate on pointers and references.  Passing an object by value will not enable virtual member functions to be invoked.

§       If any of the functions in a class are virtual, the destructor should be virtual as well.  Why?  Suppose we have a base pointer that points to a dynamically allocated derived object.  What happens when the pointer to the derived object is deleted?  If the destructor is virtual, the derived class’s destructor is called (as it should be).  The derived class’s destructor will then automatically invoke the base class’s destructor, and the entire object will be properly destroyed.  If the destructor were not virtual, then only the base class destructor would be called (Bad!).

§       Overriding – When a virtual function is changed in a derived class, the function definition is overridden.  If a non-virtual function is changed in a derived class, the function is redefined.  Both cases are treated differently by the compiler.

o      Abstract Classes and Pure Virtual Functions

§       Pure Virtual Functions are used for situations in which you want to have a class to use as a base class for a number of other classes, but you do not have any meaningful definition to give to one or more of its member functions.  For example, a Shape class might have a pure virtual Draw() function that allows derived objects of various shapes to draw shapes particular to their type.

§       An Abstract Class is a class that has one or more pure virtual functions.  A class that derives from an abstract class that does not define inherited pure virtual functions, or has pure virtual functions of its own, is also an abstract class.

·       It is not possible to instantiate an object of an abstract class.  Trying to do so will cause a compiler error.

§       Pure Virtual Functions are virtual functions that are initialized to zero within a class definition.  A class with one or more pure virtual functions is an abstract class.  The pure virtual function must be overridden in a derived class or else the derived class is also an abstract class.  This logic carries through a class hierarchy until a definition of the pure virtual function is provided.  Thus, pure virtual functions force the programmer to write a definition in a derived class.

 

 

 

 

 

 

 

// Example

#include <iostream>

#include <cmath>

using namespace std;

 

// Shape

class Shape

{

public:

      Shape();

      virtual ~Shape();

 

      virtual long GetArea() const = 0;

      virtual long GetPerim() const = 0;

      virtual void Draw() const = 0;

};

 

Shape::Shape()

{

      cout << "Shape Constructor.\n";

}

 

Shape::~Shape()

{

      cout << "Shape Destructor.\n";

}

 

// Circle

class Circle : public Shape

{

public:

      Circle(int radius);

      virtual ~Circle();

 

      virtual long GetArea() const;

      virtual long GetPerim() const;

      virtual void Draw() const;

 

private:

      int _radius;

};

 

     

Circle::Circle(int radius) : _radius(radius)

{

      cout << "Circle Constructor.\n";

}

 

Circle::~Circle()

{

      cout << "Circle Destructor.\n";

}

 

 

 

 

long Circle::GetArea() const

{

      return (3  * pow(_radius, 2));

}

 

long Circle::GetPerim() const

{

      return (2 * 3 * _radius);

}

 

void Circle::Draw() const

{

      cout << "Draw Circle Stub Function\n";

}

 

// Rectangle

class Rectangle : public Shape

{

public:

      Rectangle(int length, int width);

      virtual ~Rectangle();

 

      virtual long GetArea() const;

      virtual long GetPerim() const;

      virtual int GetLength() const;

      virtual int GetWidth() const;

      virtual void Draw() const;

 

private:

      int _length;

      int _width;

};

 

Rectangle::Rectangle(int length, int width)

      : _length(length), _width(width)

{

      cout << "Rectangle Constructor.\n";

}

 

Rectangle::~Rectangle()

{

      cout << "Rectangle Destructor.\n";

}

 

long Rectangle::GetArea() const

{

      return (_length * _width);

}

 

long Rectangle::GetPerim() const

{

      return ((2 * _length) + (2 * _width));

}

 

 

 

int Rectangle::GetLength() const

{

      return _length;

}

 

int Rectangle::GetWidth() const

{

      return _width;

}

 

void Rectangle::Draw() const

{

      for(int i = 0; i < _length; i++)

      {

            for(int j = 0; j < _width; j++)

                  cout << "* ";

 

            cout << endl;

      }

}

 

 

// Square

class Square : public Rectangle

{

public:

      Square(int length);

      Square(int length, int width);

      ~Square();

 

      virtual long GetPerim() const;

};

 

Square::Square(int length) : Rectangle(length, length)

{

      cout << "Square Constructor.\n";

}

 

Square::Square(int length, int width) :

      Rectangle(length, width)

{

      cout << "Square Constructor.\n";

 

      if(GetLength() != GetWidth())

            cout << "Error: Not a square!\n";

 

}

 

Square::~Square()

{

      cout << "Square Destructor.\n";

}

 

 

 

 

long Square::GetPerim() const

{

      return (4 * GetLength());

}

 

 

// Main

int main()

{

      int choice;

      bool quit = false;

      Shape *sPtr;

 

      while(1)

      {

            do

            {

                  cout << "(1)Circle (2)Rectangle ";

                  cout << "(3)Square (0)Quit: ";

                  cin >> choice;

 

                  switch(choice)

                  {

                  case 0:

                        quit = true;

                        break;

 

                  case 1:

                        sPtr = new Circle(5);

                        break;

 

                  case 2:

                        sPtr = new Rectangle(4,7);

                        break;

 

                  case 3:

                        sPtr = new Square(5);

                        break;

                  }

            } while((choice < 0) || (choice > 3));

 

            if(quit)

                  break;

 

            sPtr->Draw();

            delete sPtr;

 

            cout << endl;

      }

 

      return 0;

}

 

 

 

 

// Output

(1)Circle (2)Rectangle (3)Square (0)Quit: 1

Shape Constructor.

Circle Constructor.

Draw Circle Stub Function

Circle Destructor.

Shape Destructor.

 

(1)Circle (2)Rectangle (3)Square (0)Quit: 2

Shape Constructor.

Rectangle Constructor.

* * * * * * *

* * * * * * *

* * * * * * *

* * * * * * *

Rectangle Destructor.

Shape Destructor.

 

(1)Circle (2)Rectangle (3)Square (0)Quit: 3

Shape Constructor.

Rectangle Constructor.

Square Constructor.

* * * * *

* * * * *

* * * * *

* * * * *

* * * * *

Square Destructor.

Rectangle Destructor.

Shape Destructor.

 

(1)Circle (2)Rectangle (3)Square (0)Quit: 4

(1)Circle (2)Rectangle (3)Square (0)Quit: -1

(1)Circle (2)Rectangle (3)Square (0)Quit: 2

Shape Constructor.

Rectangle Constructor.

* * * * * * *

* * * * * * *

* * * * * * *

* * * * * * *

Rectangle Destructor.

Shape Destructor.

 

(1)Circle (2)Rectangle (3)Square (0)Quit: 0

Press any key to continue

Hosted by www.Geocities.ws

1