Contents  1-5  6-11  12-16  17-21  22-27   28-33  34 - 38  39-46  Projects   MFC

   Contact
   Search
   C
   C++
   Visual Basic
   Java
   JavaScript
   DHTML
   Style Sheets
   About
   Active X
   TDC Binding
   PHP
   Perl and CGI
   Flash
   XML
   SQL
   Messages
   Chat
   MCSE
   Linux
   Cabling   
   ActionScript
   Downloads
   E-Cards   
 
    
    

Polymorphism allows pointers to base classes like Mammal to be assigned to derived class objects like Cat or Human. 
Let's look at an example: 
Mammal *MammalPointer = new Cat;  This would create a new Mammal object on
the heap and return a pointer to that object which would be assigned to a pointer to Mammal.  The usefulness of this
is more clearly understood when we look at virtual methods.

"If patterns of ones and zeros were like patterns of human lives and death, if everything about an individual could be represented in a computer record by a long string of ones and zeros, then what kind of creature would be represented by a long string of lives and deaths?" - Thomas Pynchon

Virtual methods - Virtual methods are pointers to base classes that are assigned to derived class objects.  For example, if we wanted to use a pointer to access base class methods in Mammal, and yet create a Cat object on the free store, virtual methods would allow the Mammal methods that are overridden in Cat to call the correct functions in Cat, even though the pointer points to a base class Mammal object.  The functions in Mammal that would allow this would be marked with the keyword "virtual".  This allows us to dynamically allocate memory for Mammal objects when the derived Cat objects don't yet exist, and then later have their correct derived class methods called.

//Virtual methods
#include <iostream.h>

//--------------------------------------------------------------------------------------------------------------------

class
Mammal
{
      public:
      Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
      ~Mammal() { cout << "Mammal destructor...\n"; }

      void Move() const { cout << "Mammal move one step\n"; }
      virtual void Speak() const { cout << "Mammal making sound!\n"; } 

      protected:
      int itsAge;
};

//--------------------------------------------------------------------------------------------------------------------

class Squirrel : public Mammal
{
      public:
      Squirrel() { cout << "Squirrel constructor...\n"; }
      ~Squirrel() { cout << "Squirrel destructor...\n"; }

      void SwishTail() { cout << "Swishing Tail...\n"; }
      void Speak()const { cout << "Squeak!\n"; }
      void Move()const { cout << "Squirrel moves 5 steps...\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

int main()
{

    Mammal *MammalPointer = new Squirrel;
    MammalPointer->Move();
    MammalPointer->Speak();

    return 0;
}

Output:
Mammal constructor . . .
Squirrel constructor . . .
Mammal move one step
Squeak!


In the example above, Mammal is provided the VIRTUAL method "speak()".  Speak() is virtual, Move() is not.  This indicates this class expects to eventually be another class's base type.  This also signifies that the derived class will probably want to override this function.  A pointer to Mammal object is created, pSquirrel.  It is assigned the address of a new Squirrel object.  Squirrel is a Mammal - it derived from Mammal, so this is legal.  The pointer is then used to call the Move() function.  The compiler knows pSquirrel only to be a Mammal, and so looks to the Mammal object to find the Move() method.  This pointer is then used to call the "Speak()" method.  Because the Speak() method is virtual, the Speak() method in Squirrel is invoked, overriding the Mammal method's Speak().

Note: As far as the calling function knew, it had a Mammal pointer, but in this case a method from the derived Squirrel class was called.  If you had an array of pointers to Mammal, each of which pointed to a subclass of Mammal, you could call each in turn and the correct function would be called instead of the ones in Mammal, all accomplished by using the keyword "virtual".

//Multiple virtual member functions called in turn
#include <iostream.h>

//--------------------------------------------------------------------------------------------------------------------

class Monster
{
      public:
      Monster():itsAge(1) { }
      ~Monster() { }

      virtual void Speak() const { cout << "Monster speaking!\n"; }   

      protected:
      int itsAge;
};

//--------------------------------------------------------------------------------------------------------------------

class Giant : public Monster
{
      public:
      void Speak()const { cout << "Fee! Fi! Fo! Fum!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

class Ogre : public Monster
{
      public:
      void Speak()const { cout << "Grrrrrrrrrrrr!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

class Troll : public Monster
{
      public:
      void Speak()const { cout << "I hate you!\n"; }
};
//--------------------------------------------------------------------------------------------------------------------

class Dragon : public Monster
{
      public:
      void Speak()const { cout << "Hiss!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

int main()
{
    Monster *ArrayOfMonsterPointers[5];
    Monster *SingleMonsterPointer;
    int choice, i;

    for(i = 0; i<5; i++)
    {
         cout << "(1)Giant (2)Ogre (3)Troll (4)Dragon: ";
         cin >> choice;

         switch(choice)
         {
          case 1: SingleMonsterPointer = new Giant;
                  break;
          case 2: SingleMonsterPointer = new Ogre;
                  break;
          case 3: SingleMonsterPointer = new Troll;
                  break;
          case 4: SingleMonsterPointer = new Dragon;
                  break;
          default: SingleMonsterPointer = new Monster;
                   break;
         }

      ArrayOfMonsterPointers[i] = SingleMonsterPointer;
    }

    for(i = 0;i < 5; i++)
        ArrayOfMonsterPointers[i]->Speak();

    return 0;
}

 
Output:
(1)Giant (2)Ogre (3)Troll (4)Dragon: 1
(1)Giant (2)Ogre (3)Troll (4)Dragon: 2
(1)Giant (2)Ogre (3)Troll (4)Dragon: 3
(1)Giant (2)Ogre (3)Troll (4)Dragon: 4
(1)Giant (2)Ogre (3)Troll (4)Dragon: 5
Fee! Fi! Fo! Fum!
Grrrrrrrrrrrr!
I hate you!
Hiss!
Monster Speaking!

In the example listed above, the Monster class's Speak() function is declared to be virtual.  All of the classes that derive from Monster override the implementation of Speak() in Monster.  In this case, the pointer points to Monster and not the classes that derive from Monster.  The correct functions would not be called if Monster's Speak() function were not declared to be virtual.  In main, the user  is prompted for which objects to create and pointers are added to the array.  As the user makes choices, objects are dynamically created on the heap by each choice.

This is an example of Dynamic Binding (Runtime Binding), instead of Static Binding (Compile-time Binding).  At compile time, it is unknown which objects will be chosen to be created on the heap, and therefore which Speak() methods will be invoked.  The pointer "SingleMonsterPointer" is bound to the object it represents on the heap at runtime.  Again notice that, when an object that derive from Monster is created, such as an Ogre or a Troll, first the constructor for the base class is called, then the constructor for the derived class is called.  The Monster part of the object is contiguous in memory with the Ogre and Troll part.  This brings us to a couple of keywords that have to do with virtual methods:

v-table - virtual function table, keeps track of virtual functions
vptr - (v-pointer) virtual table pointer, points to v-table.

It is important to note, virtual functions only operate on pointers and references. They don't work when passing by value.  Example:

//Invoking virtual methods when passing by value?
#include <iostream.h>

//--------------------------------------------------------------------------------------------------------------------

class Monster
{
      public:
      Monster():itsAge(1) { }
      ~Monster() { }

      virtual void Speak() const { cout << "Monster speaking!\n"; }   

      protected:
      int itsAge;
};

//--------------------------------------------------------------------------------------------------------------------

class Giant : public Monster
{
      public:
      void Speak()const { cout << "Fee! Fi! Fo! Fum!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

class Ogre : public Monster
{
      public:
      void Speak()const { cout << "Grrrrrrrrrrrr!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

class Troll : public Monster
{
      public:
      void Speak()const { cout << "I hate you!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

class Dragon : public Monster
{
      public:
      void Speak()const { cout << "Hiss!\n"; }
};

//--------------------------------------------------------------------------------------------------------------------

//Function prototypes
void ValueFunction(Monster);          
//attempting to pass mammal by value parameter
void PointerFunction(Monster*);       
//pointer declared to Mammal parameter
void ReferenceFunction(Monster&);     
//reference declared to Mammal parameter

//--------------------------------------------------------------------------------------------------------------------

int main()
{
    Monster *MonsterPointer=0;
    int choice;

    while(1)
    {
        bool fQuit = false;
        cout << "(1)Giant (2)Ogre (3)Troll (4)Dragon (0)Quit: ";
        cin >> choice;

        switch(choice)
        {
           case 0: fQuit = true;
                   break;
           case 1: MonsterPointer = new Giant;
                   break;
           case 2: MonsterPointer = new Ogre;
                   break;
           case 3: MonsterPointer = new Troll;
                   break;
           case 4: MonsterPointer = new Dragon;
                   break;
           default: MonsterPointer = new Monster;
                    break;
        }

    if(fQuit)
        break;
 //Break out of the while true loop

    ValueFunction(*MonsterPointer);
    PointerFunction(MonsterPointer);
    ReferenceFunction(*MonsterPointer);

    }
//end while true loop

    return
0;

}
//close main()

//--------------------------------------------------------------------------------------------------------------------

void ValueFunction(Monster MonsterValue)
            //attempting to pass Monster by value
{  MonsterValue.Speak();  }



void PointerFunction(Monster *pMonster)          
//pointer declared to Monster
{  pMonster->Speak();  }



void ReferenceFunction(Monster &rMonster)
           //reference declared to Monster
{  rMonster.Speak();  }
Output:
(1)Giant (2)Ogre (3)Troll (4)Dragon (0)Quit: 1
Monster Speaking.
Fee! Fi! Fo! Fum!
Fee! Fi! Fo! Fum!
(1)Giant (2)Ogre (3)Troll (4)Dragon (0)Quit: 2
Monster Speaking.
Grrrrrrrrrrrr!
Grrrrrrrrrrrr!
(1)Giant (2)Ogre (3)Troll (4)Dragon (0)Quit: 3
Monster Speaking.
I hate you!
I hate you!

(1)Giant (2)Ogre (3)Troll (4)Dragon (0)Quit: 4
Monster Speaking.
Hiss!
Hiss!

1)Giant (2)Ogre (3)Troll (4)Dragon (0)Quit: 0

When the user enters a choice, a pointer, a reference, and a value are created rendering 3 lines of output.  Since virtual member functions only work with pointers and reference, the first two call the correct Speak() method from Giant that overrides the Speak() method in the base class, Monster. The third attempt tries to pass the object by value, but since virtual member functions don't work passing by value, the Speak() method from Monster is called instead of the one overridden in the Giant class.

In this example 3 functions declared, PointerFunction(), ReferenceFunction(), and ValueFunction().  They take a pointer to a Monster object, a reference to a Monster object, and a Monster object itself by value, respectively.  They all call the Speak() method.  In the first few lines the user enters 1, causing a Giant object to be created on the heap.  It is then passed as a pointer, by reference, and by value to the 3 functions.  As the user dynamically chooses objects, the pointer will dynamically bind to the correct methods for its object.

Pointers and references invoke the virtual member functions and the Giant->Speak() member function is invoked.  The dereferenced pointer is passed by value, so the compiler "SLICES" down the Giant object to just its Monster part.  The Monster Speak() method is then called.

Virtual destructors and Clone Methods- When a pointer to a derived object is deleted, the derived class's destructor is called automatically, invoking the base class's destructor.  If any functions in the class are virtual, the destructor should be as well.  It is important to note that NO constructor can be virtual! What if you want to pass in a pointer to a base object, and have a copy of the correct derived object that is created?  A solution is to create a clone method in the base class and make the method virtual.  Clone methods create a new copy of the current object and then return that object using the "this" pointer.  Because each derived class overrides the clone method, a copy of the entire derived class is created.  Example:

//Virtual copy constructor - Clone Method

#include <iostream.h>

// --------------------------------------------------------------------------------------------------------------------------

class
Employee
{
      public:
      Employee():SSN(1) { cout << "Employee constructor.\n"; }
      ~Employee() { cout << "Employee destructor.\n"; }
      Employee (const Employee & rhs);

     
//Accessor methods
      double GetSSN() const { return SSN; }
      void SetSSN(double social) { SSN = social; }
      float GetPayRate() const { return PayRate; }
      void SetPayRate(float pay) { PayRate = pay; }
     

     
//Virtual methods
      virtual void IdentifySelf() const { cout << "I am a generic Employee.\n"; }
      virtual Employee *Clone() { return new Employee(*this); }   

      protected:
      double SSN;
      float PayRate;
};

//Employee Copy Constructor defined outside the employee class.
Employee::Employee (const Employee & rhs):SSN(rhs.GetSSN())
{ cout << "Employee Copy Constructor...\n"; }


// --------------------------------------------------------------------------------------------------------------------------

class
EmpManager : public Employee
{
      public:
      EmpManager() { cout << "EmpManager constructor...\n"; }
      ~EmpManager() { cout << "EmpManager destructor...\n"; }
      EmpManager (const EmpManager & rhs);

      void IdentifySelf()const { cout << "I am an manager.\n"; }

      virtual Employee *Clone() { return new EmpManager(*this); }

     
//Accessor methods
      void SetManagerLevel(int level) {ManagerLevel = level;}
      int GetManagerLevel() { return ManagerLevel; }
      void SetPayBonus(float bonus) {PayBonus = bonus;}
      float GetPayBonus() { return PayBonus; }

      private:
      int ManagerLevel;
      float PayBonus;

};

//Manager copy constructor defined outside class
EmpManager::EmpManager(const EmpManager & rhs):
Employee(rhs)
{ cout << "EmpManager copy constructor...\n"; }

// --------------------------------------------------------------------------------------------------------------------------

class EmpClerk : public Employee
{
      public:
      EmpClerk() { cout << "EmpClerk constructor...\n";
                   PerfectAttendence = false;
                   EmployeeOfTheMonth = false;  }

      ~EmpClerk() { cout << "EmpClerk destructor...\n"; }

      EmpClerk (const EmpClerk &);

      void IdentifySelf()const { cout << "I am a clerk.\n"; }

      virtual Employee *Clone() { return new EmpClerk(*this); }

      void IfEmployeeOfTheMonth() {
           if(EmployeeOfTheMonth == true){
              cout << "\nEmployee wins Employee Of The Month status."
                   << "\nUpgrade employee\'s parking place."; }
           else { cout << "Sorry.  This employee does not meet the requirements."; }
      }
//close function
 
      private:
      bool PerfectAttendence;
      bool EmployeeOfTheMonth;
};


//Clerk copy constructor defined outside class
EmpClerk::EmpClerk(const EmpClerk & rhs):
Employee(rhs)
{ cout << "EmpClerk copy constructor...\n"; }

// --------------------------------------------------------------------------------------------------------------------------

enum EmployeeType{GenericEmployee, Manager, Clerk};
const int NumEmployeeTypes = 3;

// --------------------------------------------------------------------------------------------------------------------------

int main()
{
    Employee *EmployeePointerArray[NumEmployeeTypes];
    Employee *SingleEmployeePointer;

    int choice;
    int i;

    for(i = 0; i<NumEmployeeTypes; i++)
    {
        cout << "(1)Manager (2)Clerk (3)Employee: ";
        cin >> choice;

            switch(choice)
            {
               case Manager: SingleEmployeePointer = new EmpManager;
                             break;
               case Clerk: SingleEmployeePointer = new EmpClerk;
                           break;
               default: SingleEmployeePointer = new Employee;
                        break;
            }

        EmployeePointerArray[i] = SingleEmployeePointer;
    } 
//close for loop


    Employee *AnotherEmpPointerArray[NumEmployeeTypes];

    for(i=0; i<NumEmployeeTypes; i++)
    {
        
//Calling IdentifySelf() on a generic Employee pointer works since it's virtual and overridden in each derived class.
         EmployeePointerArray[i]->IdentifySelf();

         AnotherEmpPointerArray[i] = EmployeePointerArray[i]->Clone();
    }

    for(i=0; i<NumEmployeeTypes; i++) {
           //Calling IdentifySelf() on a generic Employee pointer works since it's virtual and overridden in each derived class.
        AnotherEmpPointerArray[i]->IdentifySelf(); }

    return 0;
}
Output:
(1)Manager (2)Clerk (3)Employee: 1
Employee constructor . . .
EmpManager constructor . . .
(1)Manager (2)Clerk (3)Employee: 2
Employee constructor . . .
EmpClerk constructor . . .
(1)Manager (2)Clerk (3)Employee: 3
Employee constructor . . .
I am a manager.
Employee copy constructor . . .
EmpManager copy constructor . . .
I am a clerk.
Employee copy constructor . . .
EmpClerk copy constructor . . .
I am an Employee.
Employee copy constructor . . .
I am a manager.
I am a clerk.

I am a generic Employee.

In the example above, the switch statement creates an EmpManager, EmpClerk or Employee object on the free store using new depending on the choice made by the user.  Doing this calls the corresponding object's constructors and copy constructors.  As the switch statement falls through, copy constructors are called in turn as the virtual Clone() method overridden in each derived class creates copies each object chosen.  On line 1 of output, the user is asked to make a choice and chooses 1, creating an EmpManager object.  The Employee and EmpManager constructors are invoked.  On line 4 of the output, the user is asked for their choice and chooses an EmpClerk object.  The Employee and EmpClerk constructors are invoked.  On line 7 of the output, 3 is chosen and an Employee object is created.  This causes the Employee constructor to be invoked. 

Line 9 of the output is produced by a call to the IdentifySelf() method on first object, EmpManager.  The IdentifySelf() method is declared virtual in the base class and is overridden in each derived class, so the correct version of IdentifySelf() is invoked by each call.  The virtual Clone() function is then called, and since it is virtual, the Manager Clone() method is called rather than the Employee Clone() methods, causing the Employee copy constructor and the EmpManager copy constructor to be called.  The same is repeated for EmpClerk and Employee on the subsequent lines of output.  Finally, a new array of 3 Employee pointers is created and the for loop iterates through each of the objects, calling the object's corresponding IdentifySelf() method. 

Let's make the observation again.  There are 3 loops here.  The 1st loop asks for input 3 times.  The 2nd loop calls the overridden and virtual IdentifySelf(), and then invokes constructors and copy constructors for each object as its Clone() method is called.  The 3rd loop, once constructors have been invoked, calls the IdentifySelf() method again for each of the 3 objects to demonstrate that they have been copied.

This is similar to previous example, but a new virtual method has been added to the Employee class - Clone().  The method, Clone(), returns a pointer to a new Employee object by calling the copy constructor and passing in itself (*this) as a const reference.   This is because it has been declared virtual in the base class and is overridden in each derived class.  EmpManager and EmpClerk both override the Clone() method, and pass copies of themselves to their own copy constructors.  Since Clone() is virtual, this will effectively create a virtual copy constructor.  The result of Clone() is a pointer to a copy of the object which is stored in a second array.

The cost of virtual methods - As usual, there is a price to pay for convenience.  In order for virtual methods to function and perform their magic, "v-tables" must be maintained that point to the right function.  In this respect, virtual methods use more memory.  But once you declare any methods virtual, most of the price in terms of memory has been paid.  In this way, they can sometimes be useful tools for reducing a program's complexity, when and if the situation calls for it.


©2004 C. Germany