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   
 
    
    

Inheritance hierarchies reflect real world relationships.  To understand our world, we break it up into bits and group
these bits together into classifications. 

"Great spirits have always found violent opposition from mediocrities. The latter cannot understand it when a man does not thoughtlessly submit to hereditary prejudices but honestly and courageously uses his intelligence." - Albert Einstein

"
He who asks is a fool for five minutes, but he who does not ask remains a fool forever." - Chinese Proverb

I. The "Has-a" Relationship

If something breathes, moves, eats and reproduces it is an Animal object.  We group together those things that  possess the characteristics of Animal objects, demonstrating the "has-a" relationship:  an Animal eats, an Animal reproduces, and an Animal is usually capable of some form of locomotion.  There are other objects in the universe that are not Animal: plants which photosynthesize and rocks which do not consume food.  For an object to be an Animal, it must possess distinctive characteristics relative to its "has-a" relationship.  There are a set of physical characteristics as well a a set of actions that correspond to Animal.   If we were going to create an Animal class, its data members and methods would be defined by the "has-a" relationship.  Example:

class Animal {
        public:
        Animal(){ }
        ~Animal() { }
   
       //Methods - actions of an animal
       void Eat() { cout << "Eating.";  }
       void Reproduce() { cout << "Reproducing."; }
       void Move() { cout << "Moving."; }

       private:
       bool hungry;
       int age;
       int weight;
       int SensoryOrgan;  
}

In the same way, an Employee object's "has-a" relationship would consist of the attributes: Social Security Number, Name, Age, Pay Rate, etc.  Its methods might consist of: Hire(), Fire(), PayRaise(), setSSN(), etc.

II. The "Is-a" Relationship


In the "is-a" relationship, objects that are grouped into larger, macroscopic classifications are broken down in to smaller and smaller groups.  A Cat object is a Mammal object, a Mammal object is an Animal object.  Derivation is a way of explaining the "is-a" relationship.  The Iguana class would inherit from the Reptile class,  and the Reptile class from the Animal class.  As an illustration, you don't have to state explicitly that Dogs move, eat, or reproduce - they inherit that from Mammal.  We might later choose to override these Mammal methods with ones more specific to Dog, but Dog inherits these methods in basic form built-in from its base class, Mammal.  A class that adds new functionality to an existing class is said to "derive" from that class.  The original class is said to be the new class's "base class".  In Java, they would be called subclass and superclass - in C++, base class and derived class.  Typically a base class has more than 1 derived class. 

Many of the examples in this section, due to lack of space and time and for the sake of brevity, use what is referred to as "stubbing out".  This is writing only enough code to show that the function was called and leaving the details for later when you have more time.  This is one way in which you can build scaffolding for your applications, around which you will later construct the entire program.

When declaring a class, indicate what class it derives from by writing a colon after the class name, the type of derivation (public, private or protected), and the class from which it derives.   Example:
class Dog : public Mammal .  The class from which you derive must have been declared earlier in your code or you will get a compiler error for trying to derive from a class that the compiler does not know exists.  Example:

//Simple inheritance
#include <iostream.h>

enum BREED { JollyGreen, Friendly, Mean, Cannibalistic, Vegetarian, Mutant };

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

class Monster
{
      public:
      Monster();
      ~Monster();

    
  //Accessor methods
      int GetStrength()const;
      void SetStrength(int str);
      int GetWeight() const;
      void SetWeight(int wgt);

     
//Other methods
      void Speak();
      void Attack();

      protected:

      int Strength;
      int Weight;
};

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

class Giant : public Monster
{
      public:

     
//Constructors
      Giant();
      ~Giant();

    
//Accessors
     BREED GetBreed() const;
     void SetBreed(BREED thebreed);

  
//Other methods
   FeFiFoFum();
   FallDownTheBeanStalk();

   protected:
   BREED itsBreed;
};

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

void main() {   }

No Output.

In this first example, the Monster class declared.  In C++ you can represent only a fraction of the information needed to represent reality, reality is far too complex to include both every base and derived class of Monster, so you include only those that will map back to reality reasonably well. In this case, the Giant class inherits from Monster.  Every Giant object will have 3 member variables: itsAge, itsWeight, and itsBreed - the combination of the data members of the base and derived class.  The class declaration for Giant does not include itsAge and itsWeight, it inherits these from Monster along with all of Monster's methods except the copy constructor, constructor and destructor.  This brings us to the inevitable discussion of three type of inheritance - that is to say three type of derivation:

1) public - Public data members and methods are available to all classes, both derived and non-derived.  They may be accessed without accessor methods.  Because of this, though easiest to code and debug, this is the least preferred method to code inheritance. 
2) protected - Protected data members are available to derived classes without having to use accessor methods, but they are not available to any other classes that have not derived specifically from the base class declaring its member to be protected.
3) private - Private data members are not available to derived classes.  If there is a public accessor method available in the base class, the derived class may use it to manipulate private data members.  If not, the base class private data members and methods are simply unavailable.

//Example of protected
#include <iostream.h>

enum EmpType { LevelOne, LevelTwo, LevelThree };

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

class Employee{
      public:
     
//Constructor and destructor
      Employee():PayRate(6.55), SSN(000000000) {
                 cout << "\nAn Employee has been created!\n"; }
      ~Employee() {cout << "Employee object being destroyed."; }

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

    
//Other methods
     void Promote()const { cout << "Give them a pay raise!\n";
                           cout << "Employee moved up one level in the food chain!\n"; }
     void Demote()const { cout << "Bump them down a level.\n"; }

     protected:
     long SSN;
     float PayRate;
};

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

class Manager : public Employee {
      public:
     
//Constructor sets EmpType to LevelOne as default
      Manager():itsEmpType(LevelOne){
                cout << "A Manager that derives from an Employee has been created!\n";}
      ~Manager(){ cout << "Manager object being destroyed."; }

     
//Accessors
      EmpType GetEmpType() const { return itsEmpType; }
      void SetEmpType(EmpType EType) { itsEmpType = EType; }

     
//Other methods
      void Hire() { cout << "It's your lucky day!\n"; }
      void Fire() { cout << "Don\'t let the door hit you on the way out.\n"; }

      private:
      EmpType itsEmpType;
};

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

int main()
{
    Employee CharlesGermany;

    cout << "Employee CharlesGermany makes $" << CharlesGermany.GetPayRate()
         << " per hour!\n";

    CharlesGermany.SetSSN(345768976);

    cout << "CharlesGermany\'s social is: " << CharlesGermany.GetSSN() << ".\n\n";


    Manager MyWonderfulBoss;

    MyWonderfulBoss.Hire();
    MyWonderfulBoss.Promote();

    MyWonderfulBoss.SetSSN(265235687);

    cout << "MyWonderfulBoss\'s social: " << MyWonderfulBoss.GetSSN() << ".\n";

    MyWonderfulBoss.SetPayRate(65.25);

    cout << "Manager MyWonderfulBoss makes $" << MyWonderfulBoss.GetPayRate()
         << " per hour!\n\n\n";

    return
0;
}

Output:

An Employee has been created!
Employee CharlesGermany makes $6.55 per hour!
CharlesGermany's social is: 345768976.

An Employee has been created!
A Manager that derives from an Employee has been created!
It's your lucky day!
Give them a pay raise!
Employee moved up one level in the food chain!
MyWonderfulBoss's social: 265235687.
Manager MyWonderfulBoss makes $65.25 per hour!

Manager object being destroyed.
Employee object being destroyed.
Employee object being destroyed.


In this example, Employee is the base class and Manager derives from Employee.  We can see that when we instantiate the Employee object, CharlesGermany, that is may use all of the methods and data members of the Employee class.  CharlesGermany may not, however, use any of the manager methods or members.  Inheritance flows from the parent to the child, not vice versa.  On the other hand, the Manager class derives from the Employee class.  In this case it can use both the methods and members of the Manager class and the Employee class.   Notice that each time a Manager is created, and Employee is created as well.  Calling the Manager constructor calls the Employee constructor.  It is the same with destructors.  Notice that as the program ends, it calls the destructor for both a Manager object and an Employee object, then it calls the destructor for and Employee object.  This is the reverse order which the constructors were called in as the objects were created.  This LIFO (Last In First Out) illustrates the storage method of objects on the stack.

Passing arguments to base constructors - In the case of the Mammal class, you may want to overload the Mammal constructor to take a specific age and the Dog constructor to take a breed.  What if Cats want to initialize weight, yet Mammals don't?  Base class initialization can be performed during class initialization by writing the base class name followed by the parameters expected by the base class.   Example:

//Overloading constructors in derived classes
#include <iostream.h>

enum BREED { RussianBlue, Persian, Siamese, Tabby, TortoiseShell, Himilayan };

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

class Mammal
{
      public:
     
//2 overloaded constructors
      Mammal();
      Mammal(int age);
      ~Mammal();

    
 //Accessors
      int GetAge() const { return itsAge; }
      void SetAge(int age) { itsAge = age; }
      int GetWeight() const { return itsWeight; }
      void SetWeight(int weight) { itsWeight = weight; }

    
 //Other methods
      void Speak() const { cout << "Mammal noise.\n"; }
      void Sleep() const { cout << "Sleeping.\n"; }

      protected:
      int
itsAge;
      int itsWeight;
};

//Define 2 overloaded Mammal Class constructors
Mammal::Mammal():
itsAge(1),
itsWeight(5)
{ cout << "Mammal constructor...\n"; }


Mammal::Mammal(int age):
itsAge(age),
itsWeight(5) { cout << "Mammal(int) constructor...\n"; }


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

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

class Cat : public Mammal
{
      public:
     
//Overloaded Constructors
      Cat();
      Cat(int age);
      Cat(int age, int weight);
      Cat(int age, BREED breed);
      Cat(int age, int weight, BREED breed);
      ~Cat();

     
//Accessors
      BREED GetBreed() const { return itsBreed; }
      void SetBreed(BREED breed) { itsBreed = breed; }

     
//Other methods
      void ChaseMice() { cout << "Here mousie mousie ... \n"; }
      void ScratchFavoriteChair() { cout << "Destroying expensive furniture ...\n"; }

      private:
      BREED itsBreed;
};


//Define 5 overloaded Cat class constructors
Cat::Cat():
Mammal(),
itsBreed(RussianBlue)
{ cout << "Cat constructor...\n"; }


Cat::Cat(int age):
Mammal(age),
itsBreed(RussianBlue)
{ cout << "Cat(int) constructor...\n"; }


Cat::Cat(int age, int weight):
Mammal(age),
itsBreed(RussianBlue)
{
    itsWeight = weight;
    cout << "Cat(int, int) constructor...\n";
}


Cat::Cat(int age, BREED breed):
Mammal(age),
itsBreed(breed)
{
    cout << "Cat(int, BREED) constructor...\n";
}


Cat::Cat(int age, int weight, BREED breed):
Mammal(age),
itsBreed(breed)
{
    itsWeight = weight;
    cout << "Cat(int, int, BREED) constructor...\n";
}


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

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

int main()
{
    Cat Felix;
    Cat Tiger(5);
    Cat Feebie(6,8);
    Cat Princess (7,Persian);
    Cat Killah (4,42,Siamese);

    Felix.Speak();
    Tiger.ChaseMice();

    cout << "Princess is " << Princess.GetAge() << " years old\n";
    cout << "Killah weighs " << Killah.GetWeight() << " pounds\n";

    return
0;
}

Output: 
Mammal constructor . . .
Cat constructor . . . 
Mammal(int) constructor . . .
Cat(int) constructor . . . 
Mammal(int) constructor . . .
Cat(int, int) constructor . . .
Mammal(int) constructor . . .
Cat(int, BREED) constructor . . .
Mammal(int) constructor . . .
Cat(int, int, BREED) constructor . . .
Mammal noise.
Here mousie mousie ...
Princess is 7 years old.
Killah weighs 42 pounds.
Cat destructor . . .
Mammal destructor . . .
Cat destructor . . .
Mammal destructor . . .
Cat destructor . . .
Mammal destructor . . .
Cat destructor . . .
Mammal destructor . . .
Cat destructor . . .
Mammal destructor . . .

In this case, Cat's default constructor calls Mammal's default constructor.  We call the base class constructor which takes no parameters in order to supply default values for Cat's base class member variables.  All of this overloading in the midst of inheritance brings us to the process of overriding functions.

©2004 C. Germany