Runtime Polymorphism in C++

    Introduction:-

    Polymorphism (पॉलिमॉर्फिज्म) से आशय "एक से अधिक रूप" से होता है। C++ में पॉलिमॉर्फिज्म दो प्रकार से हो सकता है:

    1.  Compile Time Polymorphism(कंपाइल टाइम पॉलिमॉर्फिज्म)
    2.  Runtime Polymorphism(रन टाइम पॉलिमॉर्फिज्म) 

    Compile Time Polymorphism(कंपाइल टाइम पॉलिमॉर्फिज्म)  से आशय ऐसे Polymorphism(पॉलिमॉर्फिज्म) से है जिसमें प्रोग्राम को compile(कंपाइल) करते समय ही यह निश्चित हो जाता है कि समान नाम वाले एक से अधिक Function में से कौनसा Function  रन होगा। इसे static या early binding या early linking भी कहा जाता है। यह कार्य हम Function Overloading(फंक्शन्स ओवरलोडिंग) तथा Operator Overloading(ऑपरेटर ओवरलोडिंग) के माध्यम से कर चुके हैं।

    दूसरी तरफ जब यही निर्णय प्रोग्राम के रन होने के दौरान होता है तो इसे Runtime Polymorphism(रन टाइम पॉलिमॉर्फिज्म) कहा जाता है। इसे late binding भी कहा जाता है। Runtime Polymorphism(रन टाइम पॉलिमॉर्फिज्म) का प्रयोग करने के लिए virtual function (वर्चुअल फंक्शन्स) का प्रयोग किया जाता है।

    Pointers to Derived class

    किसी Parent class(पेरेंट क्लास) का पॉइंटर parent class(पेरेंट क्लास)  के Object(ऑब्जेक्ट) के साथ-साथ Child class के ऑब्जेक्ट को भी पॉइंट कर सकता है। किन्तु इस पॉइंटर के माध्यम से सिर्फ Parent class(पेरेंट क्लास) के फंक्शन ही प्रयोग किए जा सकते हैं। 

    Example:-


        #include <iostream>
        #include <conio.h>
        using namespace std;
        class A{
            public:
                void show(){
                    cout<<"\nshow of A";
                }
        };
        class B: public A{
            public:
                void show(){
                    cout<<"\nshow of B";
                }
                void show_1(){
                    cout<<"\nshow_1 of B";
                }
        };
        int main(){
            A a, *p;
            B b;
            p = &a;  //Currently p points to a              
            p->show();
            //p->show_1;  //Not allowed

            p = &b;     // Now p points to b
            p->show();
            //p->show_1;     //Not allowed
           
            getch();
            return 0;
        }

    Output:-

    show of A
    show of A

    Explain Example:-

    उपरोक्त उदाहरण में प्रारंभ में पॉइंटर 'p' Parent class(पेरेंट क्लास) के ऑब्जेक्ट 'a' को पॉइंट कर रहा है। ऐसे में जब show () को कॉल किया गया है तो Parent class(पेरेंट क्लास) का show() ही रन हुआ। दूसरी ओर जब 'p' child class के ऑब्जेक्ट 'b' को पॉइंट कर रहा है तो भी show() को कॉल करवाने पर child class(चाइल्ड क्लास) के show () की बजाय Parent class(पेरेंट क्लास) का show() रन हुआ।

    अतः यह कहा जा सकता है कि Parent class(पेरेंट क्लास) का पॉइंटर चाइल्ड (या डिराईव्ड) क्लास को पॉइंट तो कर सकता है किन्तु उसके माध्यम से सिर्फ Parent class(पेरेंट क्लास) के फंक्शन ही प्रयोग में लिए जा सकते हैं।


    Virtual function (वर्चुअल फंक्शन)

    ऐसा संभव है कि किसी Parent class(पेरेंट क्लास) के Pointer(पॉइंटर) के माध्यम से child class(चाइल्ड क्लास) के समान नाम वाले Function को Call करवाया जा सके। इसके लिए Parent class(पेरेंट क्लास) के संबंधित फंक्शन को virtual बनाना होता है। virtual (वर्चुअल) बनाने के लिए virtual कीवर्ड का प्रयोग किया जाता है।

    उदाहरण के लिए यदि Parent class(पेरेंट क्लास) में show () नाम का Virtual Function(वर्चुअल फंक्शन) है तथा child class(चाइल्ड क्लास) में भी show() नाम का फंक्शन है, तो ऐसी स्थिति में Parent class(पेरेंट क्लास) के पॉइंटर के माध्यम से child class(चाइल्ड क्लास) के show() फंक्शन को भी Call कराया जा सकता है।

     Example:-


            #include <iostream>
            #include <conio.h>
            using namespace std;
            class A{
                public:
                    void virtual show(){
                        cout<<"\nshow of A";
                    }
            };
            class B: public A{
                public:
                    void show(){
                        cout<<"\nshow of B";
                    }
                    void show_1(){
                        cout<<"\nshow_1 of B";
                    }
            };
            int main(){
                A a, *p;
                B b;
                p = &a;  //Currently p points to a              
                p->show();
                //p->show_1;  //Not allowed

                p = &b;     // Now p points to b
                p->show();
                //p->show_1;     //Not allowed
               
                getch();
                return 0;
            }

    Output:-

    show of A
    show of B

    Explain Example:-

    उपरोक्त उदाहरण में Parent class(पेरेंट क्लास)  A में बनाया गया show() फक्शन को virtual  Declare  किया गया है ऐसा करने पर जब Parent class(पेरेंट क्लास)  के पॉइंटर p के माध्यम से child class(चाइल्ड क्लास) B के ऑब्जेक्ट b को पॉइंट करवाया गया है। चूंकि Parent class(पेरेंट क्लास)  में show () virtual function है, अतः child class(चाइल्ड क्लास) का show () function call हो जाता है। इस प्रकार यह कहा जा सकता है कि Parent class(पेरेंट क्लास) के पॉइंटर के माध्यम से child class(चाइल्ड क्लास)  के Member को virtual function (वर्चुअल फंक्शन) के माध्यम से कॉल करवाया जा सकता है।


    Pure virtual function

    Runtime Polymorphism(रनटाइप पॉलिमॉफिज्म) को प्रयोग करते समय ऐसा हो सकता है कि Parent class(पेरेंट क्लास) का ऑब्जेक्ट बनाने की कभी आवश्यकता ही नहीं पड़े। ऐसे में बनाया गया virtual function (वर्चुअल फंक्शन) कभी भी call नहीं करवाया जाएगा।

     ऐसे फंक्शन को निम्नानुसार do nothing function बनाया जा सकता है,


        class A{
            public:
                virtual void show() = 0; ///do nothing function            
        }

    ऐसे do nothing function को Pure Virtual Function(प्योर वर्चुअल फंक्शन) कहा जाता है।

    इस प्रकार कहा जा सकता है कि Pure Virtual Function(प्योर वर्चुअल फंक्शन) ऐसा Function है, जिसे Parent class(पेरेंट क्लास) में Declare तो किया जाता है, किन्तु Define नहीं किया जाता है।

    साधारणतया ऐसे फंक्शन को child class(चाइल्ड क्लास)  में Define (डिफाइन) कर दिया जाता है। अन्यथा ऐसे फंक्शन को child class(चाइल्ड क्लास) में Re-Declare(री-डिक्लेयर) करना होता है।

    जिस Class में Pure Virtual Function(प्योर वर्चुअल फंक्शन) बनाया जाता है, उसका कोई ऑब्जेक्ट नहीं बनाया जा सकता है, अतः ऐसी classes को abstract classes कहा जा सकता है। ऐसी पेरेंट क्लासेज का प्रयोग पॉइंटर बनाने तथा उसके आधार पर Runtime Polymorphism(रन टाइम पॉलिमॉर्फिज्म) का प्रयोग करने के लिए किया जाता है।

    this Pointer

    C++ की Classes में this नाम का पहले से डिफाइन किया हुआ एक Pointer होता है। यह Pointer (पॉइंटर) उस ऑब्जेक्ट को पॉइंट करता है जिसके माध्यम से Member function(मैम्बर फंक्शन) को Call करवाया जाता है। 


        #include <iostream>
        #include <conio.h>
        using namespace std;

        class EMP{
            int id, salary;
            public:
                void in_data(){
                    id = 101;       //this->id = 101 is also valid
                    this->salary = 14000;   // salary = 14000 is also valid
                }
                void show(){
                    cout<<"\nID : "<<id;
                    cout<<"\nSalary : "<<this->salary;
                }
        };
        int main(){
            EMP el;
            el.in_data();
            el.show();
            getch();
            return 0;
        }

    Output:-

    ID : 101
    Salary : 14000

    Explain Example:-

    उपरोक्त उदाहरण में जब main ( ) में e1.in_data () को Call करवाया जा रहा है तो in_data() में id=101 के माध्यम से e1 के Member id में 101 Store हो रहा है। यही कार्य this->id=101 के माध्यम से भी करवाया जा सकता है।

    दूसरी पंक्ति में this-> salary = 14000 के माध्यम से e1 के Member salary में 14000 स्टोर करवाया जा रहा है, जिसे सिर्फ salary = 14000 लिखकर भी करवाया जा सकता है।

    इस प्रकार यह स्पष्ट है कि this पॉइंटर उस Object(ऑब्जेक्ट) को पॉइंट करता है, जिसके माध्यम से Member Function(मैम्बर फंक्शन) को Call करवाया जाता है।

    उपरोक्त उदाहरण से यह तो स्पष्ट है कि this का प्रयोग कैसे किया जा सकता है, किंतु साथ ही यह प्रश्न भी उठता है कि this का प्रयोग क्यों किया जाए। यह निम्नांकित उदाहरण से स्पष्ट होता है

    Example:-


        #include <iostream>
        #include <conio.h>
        using namespace std;

        class EMP{
            int id, salary;
            public:
                void in_data(int id, int salary){
                    //id = id;      // no effect
                    this->id = id;
                    this->salary = salary;
                }
                void show(){
                    cout<<"\nID : "<<id;
                    cout<<"\nSalary : "<<this->salary;          
                }
        };
        int main(){
            EMP e1;
            e1.in_data(102, 16500);
            e1.show();
            getch();
            return 0;
        }

    Output:-

    ID : 102
    Salary : 16500

    Explain Example:-

    उपरोक्त उदाहरण में जब main () में e1.in_data( 102, 16500) को कॉल करवाया जा रहा है। तो in_data(...) के पास दो id Variable (एक Local तथा दूसरा Class वेरिएबल) है। ऐसी स्थिति में 102 तथा 16500 क्रमशः id तथा salary नाम के Local Variables में स्टोर हो गए। हैं। अब इन वैल्यूज़ को Class के id तथा salary में स्टोर करवाने के लिए Class Variables (क्लास वेरिएबल्स) के साथ this पॉइंटर का प्रयोग किया गया है। ऐसी स्थिति में this->id यह बताता है कि e1 के id मैम्बर में लोकल id वेरिएबल की वैल्यू को स्टोर कर दिया जाए।

    इस प्रकार यह कहा जा सकता है कि जब समान नाम के Class Variables(क्लास वेरिएबल) तथा Local Variable(लोकल वेरिएबल) हों तो this पॉइंटर के माध्यम से Class Variables(क्लास वेरिएबल)  को प्रयोग किया जा सकता है। वहीं अकेले Variable का नाम देने से Local Variable(लोकल वेरिएबल) को प्रयोग किया जा सकता है।

    Example:-


        #include <iostream>
        #include <conio.h>
        using namespace std;

        class EMP{
            int id;
            float salary;
            public:
                EMP(int i, int s){
                    id = i;
                    salary = s;
                }
                EMP & Higher(EMP & x){
                    if(x.salary >= this->salary){
                        return x;
                    }else{
                        return (*this);
                    }
                }
                void show(){
                    cout<<"\nID : "<<id;
                    cout<<"\nSalary : "<<this->salary;
                }
        };
        int main(){
            EMP e1(101, 14000), e2(102, 16500);
            EMP e3(0, 0);
            e3 = e1.Higher(e2);
            e1.show();
            e2.show();
            cout<<"\nHigher Salaried Emploree is : ";
            e3.show();
            getch();
            return 0;
        }

    Output:-

    ID : 101
    Salary : 14000
    ID : 102
    Salary : 16500
    Higher Salaried Emploree is : 
    ID : 102
    Salary : 16500

    Explain Example :-

    उपरोक्त उदाहरण में जब main ( ) में e3=e1.higher (e2) को Call करवाया जा रहा है तो higher(...) की definition में e2 का रेफ्रेंस EMP &x के माध्यम से x नाम के Reference Variable(रेफ्रेंस वेरिएबल) में ले लिया गया है। इसके बाद higher() की definition में if (x.salary > this->salary) के माध्यम से x.salary (अर्थात् e2.salary) तथा this->salary (अर्थात् e1 का Member ) में मध्य तुलना की गई है। यदि x (अर्थात् e2) की salary अधिक है तो x (जो स्वयं एक Reference Variable(रेफ्रेंस वेरिएबल)  है) को रिटर्न करवाया गया है, अन्यथा this के माध्यम से e1 का Reference(रेफ्रेंस) रिटर्न करवाया गया है। चूंकि रिटर्न भी Reference(रेफ्रेंस) ही है अतः EMP & higher (...) के माध्यम से Compiler को बताया जा रहा है कि Return Data Type(रिटर्न डेटा टाइप) EMP का Reference(रेफ्रेंस) हैं।

    रिटर्न कराए गए रेफ्रेंस को main() में e3 ऑब्जेक्ट में स्टोर करवाते हुए तीनों ऑब्जेक्ट्स की वैल्यू स्क्रीन पर Display की गई है।

    Post a Comment

    2 Comments

    1. What is operator Overloading please explain it

      ReplyDelete
      Replies
      1. https://www.w3codingclub.com/2022/06/operator-overloading-w3-coding-club.html

        check out this

        Delete