—
I do not know the exact type of this shape.
I do not know the exact type of this shape.
—

– Next, the code fragment:

—-
shapePtr = & c;
shapePtr -> printType ( );
—-

produces the following as output:

—-
I am a Circle with radius : 2
I do not know the exact type of this shape.
—-

-The code fragment:

—-
shapePtr = & s;
shapePtr -> printType ( );
—

produces:

—-
I am a Square with side length : 2
I do not know the exact type of this shape.
—-

3. We observe certain interesting things from the above example.

I. printType is a virtual function, while printName is not.
II. When we call shapePtr -> printType ( ), with shapePtr pointing to c (object of derived class of Shape), the printType function of class Circle is automatically called, even though shapePtr is a base class pointer.
III. Similarly, the statement shapePtr -> printType ( ), with shapePtr pointing to s (object of derived class of Shape) automatically invokes the version of printType defined in the class Square.
IV. The previous 2 observations refer to polymorphic behavior. — When a virtual function is called using a base class pointer pointing to a derived class object, the derived class’s version of the virtual function is automatically invoked.
V. The function calls to printName exhibit non-polymorphic behavior.
VI. When we call shapePtr -> printName ( ), with shapePtr pointing to s ( or c), the base class version of printName ( ) is used, unlike what had happened in the case of printType ( ).
VII. If a non-virtual member function is called using a base class pointer, the base class version of the function is used, even if the base class pointer is pointing to a derived class object. Also, if a non-virtual member function is called using a derived class pointer, the derived class version of the function is used.

4. A function once declared as virtual using the keyword virtual in its definition remains virtual throughout the inheritance hierarchy. i.e. once we declared printType as virtual in class Shape, we needn’t have declared it again as virtual in classes Square and Circle. It would have been automatically treated as virtual. However, it is a nice practice to do so since it would explicitly inform anyone reading the derived class definition that it is a virtual function.

5. The classes Square and Circle redefined the virtual function printType in their class definitions. If a derived class does not redefine a virtual function, then it inherits the definition of that function from its immediate base class.

6. When a derived class redefines a virtual function, the signature and return type of this function must be the same as in the definition of the function in the base class.

7. Abstract base classes and pure virtual functions:
There are some classes which we define just so that we may derive certain other classes from them. That is, these classes exist solely so that some other class may derive from them. Such classes, which we do not intend to ever instantiate are known as abstract base classes.

8. Trying to instantiate an object of an abstract base class results in a syntax error.

9. We make a class an abstract base class by including a pure virtual function in its definition.

A pure virtual function is defined like a virtual function with ” = 0″ added at the end of the function declaration.

e.g. The function void printType ( ) which we saw earlier, can be made pure virtual as follows:

virtual void printType ( ) const = 0;

10. Note that the base class (which is abstract since it contains pure virtual function) does not provide any implementations for a pure virtual function. It is unto each derived class to provide its own implementation of the function.

11. If a class is derived from a class having a pure virtual function, and the derived class does not redefine that virtual function, then that virtual function remains pure in the derived class. As a result, the derived class also becomes an abstract base class.