CptS 122 – Data Structures                                                                         

 

Lab 9: Polymorphism in C++

 

Assigned: Week of October 22, 2012

Due: At the end of the lab session

 

I. Learner Objectives:

 

At the conclusion of this programming assignment, participants should be able to:

 

II. Prerequisites:

 

Before starting this programming assignment, participants should be able to:

 

III. Overview & Requirements:

 

This lab, along with your TA, will help you navigate through designing, implementing, and testing polymorphism with classes in C++. It will also, once again, help you with understanding how to apply inheritance to an application.

 

Labs are held in a “closed” environment such that you may ask your TA questions. Please use your TAs knowledge to your advantage. You are required to move at the pace set forth by your TA. Please help other students in need when you are finished with a task. You may work in pairs if you wish. However, I encourage you to compose your own solution to each problem. Have a great time! Labs are a vital part to your education in CptS 122 so work diligently.

Task 1.

 

To gain a better understanding of polymorphic and virtual functions start with the following simple example. Notice we have not defined a virtual function yet.

 

// Task1.h

 

#include <iostream>

 

using std::cout;

using std::endl;

 

class Base

{

      public:

            void testFunction ();

};

 

class Derived : public Base

{

      public:

            void testFunction ();

};

 

// Task1.cpp

 

#include "Task1.h"

 

void Base::testFunction ()

{

    cout << "Base class" << endl;

}

 

void Derived::testFunction ()

{

    cout << "Derived class" << endl;

}

 

// main.cpp

 

#include "Task1.h"

 

int main(void)

{

 

    Base* ptr = new Base;

 

    ptr -> testFunction ();         // prints "Base class"

 

    delete ptr;

 

    ptr = new Derived;

 

    ptr -> testFunction ();         // prints "Base class" because the base class function is not virtual

 

    delete ptr;

 

    return 0;

}

 

Now modify the code with the following (all other code should remain the same).

 

class Base

{

      public:

            virtual void testFunction ();

};

 

 

Compile and run your program with this modification. You’ll notice the second testFunction() call generates the message “Derived class”. Welcome to polymorphism!

 

Task 2.

 

You will first build two classes, Mammal and Dog. Dog will inherit from Mammal. Below is the Mammal class code. Once you have the Mammal class built, build a second class Dog that will inherit publicly from Mammal.

 

// Mammal.h

 

#pragma once

 

#include <iostream>

 

using std::cout;

using std::endl;

 

class Mammal

{

      public:

            Mammal(void);

            ~Mammal(void);

 

            virtual void Move() const;

            virtual void Speak() const;

 

      protected:

            int itsAge;

};

 

// Mammal.cpp

 

#include "Mammal.h"

 

Mammal::Mammal(void):itsAge(1)

{

      cout << "Mammal constructor..." << endl;

}

 

Mammal::~Mammal(void)

{

      cout << "Mammal destructor..." << endl;

}

 

void Mammal::Move() const

{

      cout << "Mammal moves a step!" << endl;

}

 

void Mammal::Speak() const

{

      cout << "What does a mammal speak? Mammilian!" << endl;

}

 

Once you have completed class Mammal and Dog, build the following main program.

 

#include "Mammal.h"

#include "Dog.h"

 

int main ()

{

   Mammal *pDog = new Dog;

 

   pDog->Move();

   pDog->Speak();

 

   //Dog *pDog2 = new Dog;

 

   //pDog2->Move();

   //pDog2->Speak();

 

   return 0;

}

 

What does it output, is that what you expected?  Remove the keyword virtual from the class mammal and try it again. Now what happens? Next, put in another pointer to pDog2 in the main program, but this time make it a pointer to a Dog, not a mammal and create a new dog. Now what happens? What you should realize is that by making the method Speak virtual, we can have a little different behavior through dynamic (runtime) binding.

 

Task 3.

 

Develop additional classes for Cat, Horse, and GuineaPig overriding the move and speak methods. (If you do not know guinea pigs go “weep weep”)

 

Next, test with the modified main:

 

int main ()

{

   Mammal* theArray[5];

   Mammal* ptr;

   int choice, i;

   for (i = 0; i<5; i++)

   {

      cout << "(1)dog (2)cat (3)horse (4)guinea pig: ";

      cin >> choice;

      switch (choice)

      {

         case 1: ptr = new Dog;

         break;

         case 2: ptr = new Cat;

         break;

         case 3: ptr = new Horse;

         break;

         case 4: ptr = new GuineaPig;

         break;

         default: ptr = new Mammal;

         break;

      }

      theArray[i] = ptr;

   }

   for (i=0;i<5;i++)

      theArray[i]->Speak();

// Always free dynamically allocated objects

    for (i=0;i<5;i++)

        delete theArray[i];

   return 0;

}

 

Some things to note:

 

If the Dog object had a method, WagTail(), which is not in the Mammal, you could not use the pointer to Mammal to access that method (unless you cast it to be a pointer to Dog). Because WagTail() is not a virtual function, and because it is not in a Mammal object, you can't get there without either a Dog object or a Dog pointer to the Dog object!!!

 

The virtual function magic (polymorphic behavior) operates only on pointers and references. Passing an object by value will not enable the virtual functions to be invoked.

 

Some questions that you should start to understand:

 

Are inherited members and functions passed along to subsequent generations? If Dog derives from Mammal, and Mammal derives from Animal, does Dog inherit Animal's functions and data?

 

A. Yes. As derivation continues, derived classes inherit the sum of all the functions and data in all their base classes.

 

Q. If, in the example above, Mammal overrides a function in Animal, which does Dog get, the original or the overridden function?

 

A. If Dog inherits from Mammal, it gets the function in the state Mammal has it: the overridden function.

 

Q. Can a derived class make a public base function private?

 

A. Yes, and it remains private for all subsequent derivation.

 

Q. Why not make all class functions virtual?

 

A. There is overhead with the first virtual function in the creation of a v-table. After that, the overhead is trivial. Many C++ programmers feel that if one function is virtual, all others should be. Other programmers disagree, feeling that there should always be a reason for what you do.

 

Q. If a function (SomeFunc()) is virtual in a base class and is also overloaded, so as to take either an integer or two integers, and the derived class overrides the form taking one integer, what is called when a pointer to a derived object calls the two-integer form?

 

A. The overriding of the one-int form hides the entire base class function, and thus you will get a compile error complaining that that function requires only one int

 

Here are some more questions:

 

1. What is a v-table?

 

2. What is a virtual destructor?

 

3. How do you show the declaration of a virtual constructor?

 

4. How can you create a virtual copy constructor?

 

5. How do you invoke a base member function from a derived class in which you've overridden that function?

 

6. How do you invoke a base member function from a derived class in which you have not overridden that function?

 

7. If a base class declares a function to be virtual, and a derived class does not use the term virtual when overriding that class, is it still virtual when inherited by a third-generation class?

 

8. What is the protected keyword used for?

 

Some more exercises:

 

1. Show the declaration of a virtual function that takes an integer parameter and returns void.

 

2. Show the declaration of a class Square, which derives from Rectangle, which in turn derives from Shape.

 

3. If, in Exercise 2, Shape takes no parameters, Rectangle takes two (length and width), but Square takes only one (length), show the constructor initialization for Square.

 

4. Write a virtual copy constructor for the class Square (in Exercise 3).

 

5. BUG BUSTERS: What is wrong with this code snippet?

void SomeFunction (Shape);

Shape * pRect = new Rectangle;

SomeFunction(*pRect);

 

6. BUG BUSTERS: What is wrong with this code snippet?

class Shape() { 

public: 

Shape(); 

virtual ~Shape(); 

virtual Shape(const Shape &); 

};

 

IV. Submitting Labs:

 

V. Grading Guidelines: