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   
 
    
    

  "They have computers, and they may have other weapons of mass destruction." -  Janet Reno

"
Computer Science:  1. A study akin to numerology and astrology, but lacking the precision of
the former and the success of the latter.  2. The boring art of coping with a large number of
trivialities.
" - Stan Kelly-Bootle

We have observed that if a base class has a method "Speak()", and that method is overridden in it's derived class, a pointer to a base object that is assigned to a derived object will simply "do the right thing".   Example:

//Virtual methods
#include <iostream.h>

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

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

      virtual void Speak() const { cout << "It\'s a Monster!\n"; }

      protected:
      int itsAge;
};

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

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

      void Speak() const { cout << "Fe! Fi! Fo! Fum!\n”; }
};

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

int main()
{
    Monster *PointerToEvilGiant = new Giant;
    PointerToEvilGiant->Speak();
 
    return 0;

}

In the Giant class, what happens if you want to add a method to Giant that is inappropriate for Monster?  Perhaps something like the method SquashLittlePeopleLikeABug(), which Giants may do, but many other Monsters may not be capable of.  Example:

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

      void Speak() const { cout << "Fe! Fi! Fo! Fum!\n”; }
      void SquashLittlePeopleLikeABug() { cout << "Crush!  Kill!  Destroy!"; }
};
 

If you called SquashLittlePeopleLikeABug() on your Monster pointer, you would get a compiler error:

error C2039: 'SquashLittlePeopleLikeABug' : is not a member of 'Monster'

When the compiler tries to resolve SquashLittlePeopleLikeABug() in its virtual table, there will be no entry.  If you have a pointer to a base class assigned to a derived class, it should be because you intend to use that object polymorphically.  Don't try accessing methods specific to a derived class with a base class pointer.  As usual, there is a work around, a method of cheating. You might cast a base class pointer to a derived type.  To do this you need the dynamic_cast operator. 

dynamic_cast operator - The dynamic_cast operator ensures casting safely, but it is considered by many to be passé and unpredictable.   The purpose of virtual functions is to allow the virtual table, rather than the programmer, to determine the runtime type of the object.  This helps you quickly find places in your code where you have used this feature and remove them.  If you have a pointer to a base class, such as Monster, and you assign to it a pointer to a derived class, such as Giant, you can use the Monster pointer polymorphically.  If you then need to get at the Giant object to call the SquashLittlePeopleLikeABug() method, you create a Giant pointer and use the dynamic_cast operator to make the conversion.  At runtime the base pointer is examined.  If the conversion is proper, the new Giant pointer is fine, if not, or if you don't have a Giant object, the new pointer will be NULL.  The dynamic_cast is complemented by the static_cast operator and functions like so:

dynamic_cast<type-id>( expression ) - It converts the operand (expression) to the object of type <type-id>.  The <type-id> must be a pointer or a reference to a pre-defined class. 

static_cast<type-id>( expression ) -  It converts the operand (expression) to the object of type <type-id> without checking to make sure the conversion is safe.  It is useful for converting a pointer to a base class object into a pointer to a derived class object.  It can also convert enums to ints and ints to floats. 

Example (Note: Depending on the patch/version, this may not function the same among different compilers)

//Dynamic cast
#include <iostream.h>

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

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

      virtual void Speak() const { cout << "Mammal speak!\n"; }

      protected:
      int itsAge;
};

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

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

      void Speak()const { cout << "Meow\n"; }
      void Purr() const { cout << "rrrrrrrrrrr\n"; }
};

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

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

      void Speak() const { cout << "Woof!\n"; }
};

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

int main()
{
    const int NumberMammals = 3;
    int choice;
    int i;

    Mammal *Zoo[NumberMammals];
    Mammal *pMammal;

   
    for(i=0; i<NumberMammals; i++)
    {
         cout << "(1)Dog (2)Cat: ";
         cin >> choice;

            if (choice == 1)
                 pMammal = new Dog;
            else
          
      pMammal = new Cat;

         Zoo[i] = pMammal;
    }

    cout << "\n";


    for(i=0; i<NumberMammals; i++)
    {
         Zoo[i]->Speak();
         Cat *pRealCat = 0;

         pRealCat = dynamic_cast<Cat*>(Zoo[i]);

            
//pRealCat will be NULL if not pointing to a Cat but a Dog
             if (pRealCat)
                   pRealCat->Purr();
             else
                   cout << "Uh oh, not a cat!\n";

         delete Zoo[i];
         cout << "\n";
    }

    return 0;
}

In this example there are two loops.  In the first, the user is asked to add a Cat or Dog object to the array of Mammal pointers in a loop that iterates three times.  This first loop calls the Mammal, Dog and Cat constructors as each object is chosen by the user and entered into an array.  The second loop uses an array to call the appropriate Speak() method from Dog and Cat that overrides the one in Mammal.  A pointer to Cat is created, "pRealCat".  It is initialized to 0 and dynamic_cast is used to make sure that the object the Purr() method is called on is a Cat and not something else for each element of Zoo[i].  If the object is a Cat, the pointer will not be NULL, and will pass the test and cout << "rrrrrrrrr".  If the object is not a Cat, the pointer will be NULL, and so it will fail the test and cout << "Uh oh, not a cat!\n".   Here's another example:

#include <iostream.h>

//---------------------------------------------------------------------------------------------------------------------
class B { }; //---------------------------------------------------------------------------------------------------------------------
class C : public B { }; //---------------------------------------------------------------------------------------------------------------------
class D : public C { }; //---------------------------------------------------------------------------------------------------------------------
 
void f(D *pd)
{
  C *pc = dynamic_cast<C*>(pd);         // ok: C is a direct base class
                                        // pc points to C subobject of pd
  B *pb = dynamic_cast<B*>(pd);         // ok: B is an indirect base class
                                        // pb points to B subobject of pd
  
}

//---------------------------------------------------------------------------------------------------------------------
void main() { 
     D DObject;
     f(&DObject);
}

Example of a hierarchy of classes:

//Shape classes

#include <iostream.h>
enum BOOL { FALSE, TRUE }; 
//defining our own BOOL, instead of bool

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

class Shape
{
      public:
      Shape(){}
      ~Shape(){}

     
//Virtual methods to be overridden in each derived class
      virtual long GetArea() { return -1; }  
//Error, it's virtual, base class method never meant to be used
      virtual long GetPerim() { return -1; }
      virtual void Draw() {}
      private:
};

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

class Circle : public Shape
{
      public:
      Circle(int radius):itsRadius(radius){}
      ~Circle(){}

      long GetArea() { return 3 * itsRadius * itsRadius; }
      long GetPerim() { return 9 * itsRadius; }
 
      void Draw();

      private:
      int itsRadius;
      int itsCircumference;
};

//Draw() method defined outside class
void Circle::Draw()
{ cout << "Circle drawing routine here!\n"; }

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

class Rectangle : public Shape
{
      public:
      Rectangle(int len, int width):itsLength(len), itsWidth(width){}
      ~Rectangle(){}

      virtual long GetArea() { return itsLength * itsWidth; }
      virtual long GetPerim() {return 2*itsLength + 2*itsWidth; }
      virtual int GetLength() { return itsLength; }
      virtual int GetWidth() { return itsWidth; }
      virtual void Draw();

      private:
      int itsWidth;
      int itsLength;
};

//Draw() method defined outside class
void Rectangle::Draw()
{
     for(int i = 0; i<itsLength; i++)
     {
          for(int j = 0; j<itsWidth; j++) {
              cout << "x "; }

          cout << "\n";
     }
}

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

class Square : public Rectangle
{
      public:
      Square(int len);
      Square(int len, int width);
      ~Square(){}

      long GetPerim() { return 4 * GetLength(); }
};

//Define outside of class the overloaded constructor that takes one parameter
Square::Square(int len):Rectangle(len,len) { }


//Define outside of class the overloaded constructor that takes two parameters
Square::Square(int len, int width):Rectangle(len,width)
{
        if(GetLength() != GetWidth())
            cout << "Error, not a square... a Rectangle??\n";
}

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

int main()
{
    int choice;
    BOOL fQuit = FALSE;
    Shape *sp;

    while(1)
    {
       cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
       cin >> choice;

       switch(choice)
       {
          case 1: sp = new Circle(5);
                  break;
          case 2: sp = new Rectangle(4,6);
                  break;
          case 3: sp = new Square(5);
                  break;
          default: fQuit = TRUE;
                   break;
       }

    if (fQuit)
         break;

    sp->Draw();

    cout << "\n";

    }
//close while true loop

    return 0;
}
Output:
(1)Circle (2)Rectangle (3)Square (0)Quit: 1
Circle drawing routine here!

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

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

In this case, the classes Circle, Square and Rectangle derive from the base class Shape and override its virtual methods. 

ADT - Abstract Data Type.  An ADT, or Abstract Data Type, represents an ambiguous or vague concept like Shape, rather than an object of physical reality like Circle.  ADT's are always base classes to other classes.  You can not make an instance of a class that is an ADT.  You declare a class to be an ADT by including 1 or more pure virtual functions in the class declaration.

Pure Virtual Functions - Pure virtual functions are used to create ADT's.  Virtual functions are made pure by initializing them with 0.  Example:
virtual void Draw() = 0;  Once a class contains a pure virtual method, we can assume certain implications:

  • The pure virtual method will NEVER be used, and it must ALWAYS be overridden in each derived class.
  • The class that contains it is an ADT, and the base class for derived classes.
  • Since the class is an ADT, there will NEVER be an instance or object of that class, only of derived classes.

Any class deriving from an ADT inherits the pure virtual function as pure and so must override every pure virtual function in order to instantiate objects.  If Rectangle inherits from Shape, and Shape has 3 pure virtual functions, then Rectangle must override all 3 of those pure virtual functions, else it will be an ADT itself.  An example of making virtual functions pure:

class Shape
{
      public:
      Shape(){}
      ~Shape(){}

     
//Pure virtual functions are virtual methods initialized to 0.  This makes the class an ADT.
      virtual
long GetArea() = 0
      virtual long GetPerim()= 0;
      virtual void Draw() = 0;

      private:
};

Again, let's restate that typically pure virtual functions are never implemented, no objects are created that use them, and the ADT works purely as the definition of an interface to objects that derive from it.   The ADT never created objects of itself, only its derived classes have instances.  In this way it functions more as an organizational tool of the "Is-A/Has-A" relationship than an actual, working class structure.  You can provide an implementation to a pure virtual function, so that it can be called by objects derived from the ADT.  You might do this to provide common functionality to all of the overridden functions.  Below Shape is an ADT, the Circle class overrides Draw(), and then chains up to the base class function for additional functionality.  Example:

//Implementing pure virtual functions

#include <iostream.h>
enum BOOL { FALSE, TRUE };

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

class Shape
{
      public:
      Shape(){}
      ~Shape(){}

     
//Pure virtual methods - each will HAVE to be overridden in derived classes.  This makes Shape an ADT.
      virtual long GetArea() = 0;   
 //Error unless overridden in each derived class.
      virtual long GetPerim()= 0;
      virtual void Draw() = 0;

      private:
};

void Shape::Draw()
{ cout << "Abstract drawing mechanism!  Never used.\n"; }

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

class Circle : public Shape
{
      public:
      Circle(int radius):itsRadius(radius){}
      ~Circle(){}

     
//We MUST override the 3 functions below since they were declared pure in the base class.
      long GetArea() { return 3 * itsRadius * itsRadius; }
      long GetPerim() { return 9 * itsRadius; }
      void Draw();

      private:
      int itsRadius;
      int itsCircumference;
};

void Circle::Draw()
{
     cout << "Circle drawing routine here!\n";
     Shape::Draw();
}

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

class Rectangle : public Shape
{
      public:
      Rectangle(int len, int width):itsLength(len), itsWidth(width){}
      ~Rectangle(){}

      long GetArea() { return itsLength * itsWidth; }
      long GetPerim() {return 2*itsLength + 2*itsWidth; }


      virtual int GetLength() { return itsLength; }
      virtual int GetWidth() { return itsWidth; }
      void Draw();

      private:
      int itsWidth;
      int itsLength;
};

void Rectangle::Draw()
{
     for (int i = 0; i<itsLength; i++)
     {
         for (int j = 0; j<itsWidth; j++) {
              cout << "x "; }
         cout << "\n";
     }

     Shape::Draw();
}

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

class Square : public Rectangle
{
      public:
     
//2 overloaded constructors
      Square(int len);
      Square(int len, int width);
      ~Square(){}

      long GetPerim() { return 4 * GetLength(); }
};

Square::Square(int len):Rectangle(len,len) { }

Square::Square(int len, int width):Rectangle(len,width)
{
    if(GetLength() != GetWidth())
         cout << "Error, not a square... a Rectangle??\n";
}

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

int main()
{
    int choice;
    BOOL fQuit = FALSE;
    Shape *sp;

    while(1)
    {
       cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
       cin >> choice;

       switch(choice)
       {
          case 1: sp = new Circle(5);
                  break;
          case 2: sp = new Rectangle(4,6);
                  break;
          case 3: sp = new Square(5);
                  break;
          default: fQuit = TRUE;
                   break;
       }

    if (fQuit)
         break;

    sp->Draw();

    cout << "\n";

    }
//close while true loop

    return 0;
}

The output of the example above is the same as the output of the previous example.  However, the structure is different - the Shape class has now become an ADT.  If only one function is declared pure virtual, the class is an ADT.  Circle and Rectangle override Draw(), but chain up to the base method to take advantage of shared functionality.  You can derive ADTs from other ADTs.  In the code below, no Animal objects or Mammal objects can be instantiated because they are both base classes and both are ADTs.  However, all Mammals may inherit the Reproduce() method without overriding it.

//Deriving ADTs from other ADTs

#include <iostream.h>

enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown } ;
enum BOOL { FALSE, TRUE };

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

class Animal            
//ADT. Common base to both horse and fish.
{
      public:
      Animal(int);
      virtual ~Animal() { cout << "Animal destructor...\n"; }

     
//Virtual methods
      virtual int GetAge() const { return itsAge; }
      virtual void SetAge(int age) { itsAge = age; }

     
//Pure virtual methods
      virtual void Sleep() const = 0;
      virtual void Eat() const = 0;
      virtual void Reproduce() const = 0;
      virtual void Move() const = 0;
      virtual void Speak() const = 0;

      private:
      int itsAge;
};

Animal::Animal(int age):itsAge(age)
{ cout << "Animal constructor...\n"; }

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

class Mammal : public Animal         
//ADT.  Multiple inheritance.
{
      public:
      Mammal(int age):Animal(age)
             { cout << "Mammal constructor...\n"; }
      ~Mammal() { cout << "Mammal destructor...\n";}

      virtual void Reproduce() const 
              { cout << "Mammal reproducing!\n"; }
};

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

class Fish : public Animal
{
      public:
      Fish(int age):Animal(age)
          { cout << "Fish constructor...\n";}
      virtual ~Fish() {cout << "Fish destructor...\n"; }

        //Virtual Methods.  Fish class MUST override the 5 pure virtual methods of Animal base class.
      virtual void Sleep() const { cout << "fish snoring...\n"; }
      virtual void Eat() const { cout << "fish feeding...\n"; }
      virtual void Reproduce() const { cout << "fish laying eggs...\n"; }
      virtual void Move() const { cout << "fish swimming...\n"; }
      virtual void Speak() const { }
};

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

class Horse : public Mammal
{
      public:
      Horse(int age, COLOR color ):Mammal(age), itsColor(color)
            { cout << "Horse constructor...\n"; }
      virtual ~Horse() { cout << "Horse destructor...\n"; }

      virtual void Speak() const { cout << "Whinny!... \n"; }
      virtual COLOR GetItsColor() const { return itsColor; }
      virtual void Sleep() const { cout << "Horse snoring...\n"; }
      virtual void Eat() const { cout << "Horse feeding...\n"; }
      virtual void Move() const { cout << "Horse running...\n";}

      protected:
      COLOR itsColor;
};

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

class Dog : public Mammal
{
      public:
      Dog(int age, COLOR color ):
      Mammal(age), itsColor(color) { cout << "Dog constructor...\n"; }
      virtual ~Dog() { cout << "Dog destructor...\n"; }

      virtual void Speak()const { cout << "Whoof!... \n"; }
      virtual void Sleep() const { cout << "Dog snoring...\n"; }
      virtual void Eat() const { cout << "Dog eating...\n"; }
      virtual void Move() const { cout << "Dog running...\n"; }
      virtual void Reproduce() const { cout << "Dogs reproducing...\n"; }

      protected:
      COLOR itsColor;
};

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

int main()
{
    Animal *pAnimal=0;
    int choice;
    BOOL fQuit = FALSE;

    while(1)
    {
        cout << "(1)Dog (2)Horse (3)Fish (0)Quit: ";
        cin >> choice;

        switch (choice)
        {
             case 1: pAnimal = new Dog(5,Brown);
                     break;
             case 2: pAnimal = new Horse(4,Black);
                     break;
             case 3: pAnimal = new Fish (5);
                     break;
             default: fQuit = TRUE;
                      break;
        }
   
        if(fQuit)
           break;

        pAnimal->Speak();
        pAnimal->Eat();
        pAnimal->Reproduce();
        pAnimal->Move();
        pAnimal->Sleep();
        delete pAnimal;
        cout << "\n";
    }
//close while true loop

    return 0;
}
Output:
(1)Dog (2)Horse (3)Fish (0)Quit: 1
Animal constructor...
Mammal constructor...
Dog constructor...
Whoof!...
Dog eating...
Dogs reproducing...
Dog running...
Dog snoring...
Dog destructor...
Mammal destructor...
Animal destructor...

(1)Dog (2)Horse (3)Fish (0)Quit: 2
Animal constructor...
Mammal constructor...
Horse constructor...
Whinny!...
Horse feeding...
Mammal reproducing!
Horse running...
Horse snoring...
Horse destructor...
Mammal destructor...
Animal destructor...

(1)Dog (2)Horse (3)Fish (0)Quit: 3
Animal constructor...
Fish constructor...
fish feeding...
fish laying eggs...
fish swimming...
fish snoring...
Fish destructor...
Animal destructor...

(1)Dog (2)Horse (3)Fish (0)Quit: 0

 

Here, Mammal overrides the Animal Reproduce() method to provide a common method of reproduction for all Mammals.  The Fish class must override Reproduce(), since it derives directly from Animal and can not use Mammalian reproduction.  Remember:

1. Use ADTs to provide common functionality to related classes.
2. Override all pure virtual functions.
3. If possible, make pure virtual any function that must be overridden.
4. Don't instantiate an object of an ADT.  It is an abstract, ambiguous base class only.


©2004 C. Germany