How do runtime polymorphisms exactly make code flexible at runtime?

I am new to programming and have learned that runtime polymorphism makes code flexible.
I know that runtime polymorphism avoids code duplication and increases code reusability.
But how exactly it makes code behave DIFFRENTLY at runtime (is it really runtime).
It is true that function calls are not resolved till runtime but why we treat “not having knowledge of function address at compile time" as increase in behavioral flexibility of code.
Please provide a suitable practical example to justify this concept. Please do not reply with traditional animal, cat and dog hierarchy... I have surfed a lot of this for quite a time.
Thanks in advance :)
You'll see that animals are better than this example ;^)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Base // abstract class
{
    virtual void foo() = 0;
    ~virtual() {}
};
struct D1 : public Base
{
     void foo() { cout << "D1"; }
};
struct D2 : public Base
{
    void foo() { cout << "D2"; }
};
struct D3 : public Base
{
    void foo() { cout << "Bad User!"; }
};
int main()
{
     Base *ptr;
     cout << "Do you want D1 or D2? ";
     int whichD = 0;
     cin >> whichD;
     if ( whichD == 1 )
        ptr = new D1;
     else if ( whichD == 2 )
        ptr = new D2;
     else
        ptr = new D3; // make the user feel sorry for entering invalid number

     ptr->foo(); // this function call will depend on what the user gave as input. Cannot be determined statically

     delete ptr;
}

If the structs weren't polymorphic, you'd have to keep track of the type and cast ptr every time you wanted to call a method
1
2
3
4
struct Base
{
    // now no virtual method / destructor           
};
31
32
33
34
35
36
37
38
if ( whichD == 1 )
    dynamic_cast<D1*>(ptr)->foo();
else if ( whichD == 2 )
    dynamic_cast<D2*>(ptr)->foo();
else
    dynamic_cast<D3*>(ptr)->foo();

delete ptr; // won't call derived class destructors 
Last edited on
I really don't know what to tell you. The word flexible is flexible :) The subject is so big.

There are many circumstances when you consider using polymorphism, and it is not used alone and is rarely the only option under consideration:
- allows you to componentize a system, which improves the software architecture
- provides run-time genericity, which as with all genericity, improves reusability and decreases code duplication
- supports certain language idioms
- provides alternative to using callbacks when single callback message is not adequate
And others.

I don't know what interests you - the technical, instrumental, conceptual value of polymorphism.

Here is a very generic example. You have container document for different types of objects. The objects and the document can be visualized and the user can interact with them. Each has its own visualization operations, user input callbacks, etc. The container document has a common interface to all objects it hosts - be it table, picture, video, etc. It only has to be able to access the relevant functionality - delegate the response to some of the user actions to the objects using the designated callbacks, call the visualization procedures of the objects when it needs to draw its own canvas, etc. The interface can be provided in some abstract base class from which all host-able objects derive. The flexibility comes from the fact that the layout, hit detection, input management of the container document are not concerned with the types of objects that it hosts. They may be containers in their own right (of the same or another variety). Consequently, you can add new types and thus extend the application, without modifying the code of the container. It will decide how to handle the objects at run-time, by connecting to their operations with polymorphism.

Don't make me code this particular example. It may take a week. If you really want some example, I can try, but the last post in which I used polymorphism is virtually non-digestible for beginner. If you want it (good or bad example, you decide) - http://www.cplusplus.com/forum/general/37874/#msg203397

Remember though, that everything has certain price. Polymorphism (unlike templates and duck-typing) is relatively intrusive. It affects the type hierarchy and affects the future programming style. Also, you need to find the common denominator between different types of objects, which may lead to compromises with the efficiency and effectiveness of the solution.

Most design patterns (if you haven't heard of them) are centered around polymorphism. There are at least 20 or so of them, so it is a good place to start exploring. The above example (supposedly) incorporated a few.

Regards
a window manager.

you can create destroy many types of windows at run time.
you can have a stack of different window types, dialogs, file browsers, etc
at run time you cannot know what window types will be needed.


Polymorphism let's you categorize things and treat similar things all the same way even though they're different.

Another classic example is a file. (Although this example is a lot more functional than a Animal/Dog/Cat example)

Say you have an abstract 'File' class which has Read/Write functions for file IO.

Now let's say you have several derived classes:

- DiskFile (a normal file on the computer)
- NetworkFile (a file accessed via a network connection)
- ZipFile (a file in a .zip archive)
- RarFile (a file in a .rar archive)
etc

The beautiful thing about this is it allows you to treat all these different kinds of files the same way. It even lets you write code that is "blind" as to what kind of file it's actually using. Say for example you want to write a function that reads some information.

1
2
3
4
5
6
7
void MyClass::ReadDataFromFile( File* f )
{
  somedata = f->Read();
  moredata = f->Read();
  otherdata = f->Read();
  // etc
}


Simple and straightforward. But the cool thing about it... is that since 'File' is an abstract class, it can read from any kind of file. Whether it be a ZipFile, a NetworkFile... or whatever. It doesn't matter what kind of file it is, this code will work with it.


This also "future proofs" your code in a sense. For example the above code not only works with existing File types, but if new File types are created later, it will automatically work with them as well. You won't even have to change any of the above code.
Last edited on
@Everyone: Thank you for helping me.

So from above discussion I got that, the code of type

BaseClassPtr = new SomeClass();

Will not be genric and need to take form of all available (to be operated) classes in hierarchy,

Eg: if(Dog)
animalPtr = new Dog();
if(Cat)
animalPtr = new Cat();
....

but the code..

BaseClassPtr->CommentInterfaceMethod(); /* This code is generic or in my words Flexible :)*/

Will be generic and will act accordingly at runtime as per the object pointed out by above non generic statement or some similar statement.
Last edited on
What do you mean by the first statement? There is no virtual function call there, but one could occur as you have a BaseClass pointer that could use a virtual function redefined in SomeClass.
@jackjack Your code snippet actually depicts the whole point of polymorphism. The term "generic" means nothing else than "refering to all members of a kind". In this case Dog and Cat are all part of Animal, so if your refering to Animal (which is animalPtr), you could either mean a cat or a dog - just like Ibuprofen is Ibuprofen, no matter if it's produced by Bayer or Merck. ;) Zhuge is right in saying that there is no virtual function call, but being able to use assign "new Dog()" to "animalPtr" is something generic on its face and so is invoking a common interface function.

To sum the concept up again: Speaking in C++ terms, a pointer to an instance of a class derived from some base class, whether abstract or not, can be used as a pointer to its base class. The common interface, the overloaded virtual functions that is, can then be called when using the base class pointer. That's true genericity! One pointer to rule them all - so to speak. ;)

Maybe this example will clear it all up:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// it doesn't matter which animal - it only needs to be derived from class Animal
// if this didn't work, you'd have to write one function FOR EACH type of animal
void checkWeight(const Animal* animalPtr) 
{
    // lookup correct functions AT RUNTIME
    if(animalPtr->weight() > 10 && animalPtr->type() == CAT) 
        callVet(animalPtr);
}

Animal* animalPtr = NULL;

if(Dog)
    animalPtr = new Dog(); // base pointer is assigned a pointer to derived object  
if(Cat)
    animalPtr = new Cat();

// pure virtual function implemented by Cat and Dog - see below - look up AT RUNTIME
animalPtr->makeNoise(); 

// pointer to either dog or cat passed as a pointer to their base class
checkWeight(animalPtr); 

// snippet of Animal.h
class Animal
{
   ...

    public:
        // pure virtual function declarations - no implementation (definition) needed
        // makes Animal abstract => cannot instantiate Animal directly!
        // A class becomes abstract as soon as one or more pure virtual functions are declared
        virtual void makeNoise() = 0; 
        virtual int weight() = 0;
};


HTH. If you want to get deeper into the workings of runtime polymorphism you could start by learning how a compiler handles it: http://en.wikipedia.org/wiki/Virtual_method_table . Studying this a little further will also give you some clues when NOT to use polymorphism.

Thomas
Topic archived. No new replies allowed.