c++ सी++ में हमें आभासी कार्यों की आवश्यकता क्यों है?




सी प्लस प्लस (16)

मैं सी ++ सीख रहा हूं और मैं सिर्फ आभासी कार्यों में जा रहा हूं।

जो मैंने पढ़ा है (पुस्तक और ऑनलाइन में), वर्चुअल फ़ंक्शंस बेस क्लास में फ़ंक्शन हैं जिन्हें आप व्युत्पन्न कक्षाओं में ओवरराइड कर सकते हैं।

लेकिन पुस्तक में पहले, मूल विरासत के बारे में सीखते समय, मैं virtual उपयोग किए बिना व्युत्पन्न कक्षाओं में आधार कार्यों को ओवरराइड करने में सक्षम था।

तो मैं यहाँ क्या याद कर रहा हूँ? मुझे पता है कि आभासी कार्यों के लिए और भी कुछ है, और यह महत्वपूर्ण लगता है इसलिए मैं स्पष्ट होना चाहता हूं कि यह वास्तव में क्या है। मुझे बस एक सीधा जवाब ऑनलाइन नहीं मिल रहा है।


आभासी समारोह की आवश्यकता समझाया [समझने में आसान]

#include<iostream>

using namespace std;

class A{
public: 
        void show(){
        cout << " Hello from Class A";
    }
};

class B :public A{
public:
     void show(){
        cout << " Hello from Class B";
    }
};


int main(){

    A *a1 = new B; // Create a base class pointer and assign address of derived object.
    a1->show();

}

आउटपुट होगा:

Hello from Class A.

लेकिन वर्चुअल फ़ंक्शन के साथ:

#include<iostream>

using namespace std;

class A{
public:
    virtual void show(){
        cout << " Hello from Class A";
    }
};

class B :public A{
public:
    virtual void show(){
        cout << " Hello from Class B";
    }
};


int main(){

    A *a1 = new B;
    a1->show();

}

आउटपुट होगा:

Hello from Class B.

इसलिए वर्चुअल फ़ंक्शन के साथ आप रनटाइम पॉलीमोर्फिज्म प्राप्त कर सकते हैं।


"आभासी" के बिना आपको "प्रारंभिक बाध्यकारी" मिलता है। विधि के कार्यान्वयन का उपयोग उस पॉइंटर के प्रकार के आधार पर संकलित समय पर तय किया जाता है जिसे आप कॉल करते हैं।

"आभासी" के साथ आपको "देर से बाध्यकारी" मिलता है। विधि का कौन सा कार्यान्वयन उपयोग किया जाता है, बिंदु-बिंदु वस्तु के प्रकार के आधार पर रन टाइम पर निर्णय लिया जाता है - जिसे मूल रूप से बनाया गया था। यह आवश्यक नहीं है कि आप उस ऑब्जेक्ट को इंगित करने वाले सूचक के प्रकार के आधार पर क्या सोचेंगे।

class Base
{
  public:
            void Method1 ()  {  std::cout << "Base::Method1" << std::endl;  }
    virtual void Method2 ()  {  std::cout << "Base::Method2" << std::endl;  }
};

class Derived : public Base
{
  public:
    void Method1 ()  {  std::cout << "Derived::Method1" << std::endl;  }
    void Method2 ()  {  std::cout << "Derived::Method2" << std::endl;  }
};

Base* obj = new Derived ();
  //  Note - constructed as Derived, but pointer stored as Base*

obj->Method1 ();  //  Prints "Base::Method1"
obj->Method2 ();  //  Prints "Derived::Method2"

संपादित करें - यह प्रश्न देखें।

इसके अलावा - इस ट्यूटोरियल में सी ++ में शुरुआती और देर से बाइंडिंग शामिल है।


जब आपके पास बेस क्लास में कोई फ़ंक्शन होता है, तो आप व्युत्पन्न कक्षा में इसे Redefine या Override कर सकते हैं।

एक विधि को फिर से परिभाषित करना : व्युत्पन्न वर्ग में बेस क्लास की विधि के लिए एक नया कार्यान्वयन दिया जाता है। Dynamic binding सुविधा नहीं है

एक विधि ओवरराइड करना : व्युत्पन्न कक्षा में बेस क्लास की virtual method को फिर से Redefining करना। वर्चुअल विधि गतिशील बाध्यकारी की सुविधा प्रदान करता है

तो जब आपने कहा:

लेकिन पुस्तक में पहले, मूल विरासत के बारे में सीखते समय, मैं 'वर्चुअल' का उपयोग किये बिना व्युत्पन्न कक्षाओं में आधार विधियों को ओवरराइड करने में सक्षम था।

आप इसे ओवरराइड नहीं कर रहे थे क्योंकि बेस क्लास में विधि वर्चुअल नहीं थी, बल्कि आप इसे फिर से परिभाषित कर रहे थे


मैं वर्चुअल फ़ंक्शन का एक और उपयोग जोड़ना चाहता हूं हालांकि यह उपर्युक्त उत्तरों के समान अवधारणा का उपयोग करता है लेकिन मुझे लगता है कि इसका उल्लेख उल्लेखनीय है।

वर्टिकल डिस्ट्रक्टर

वर्चुअल के रूप में बेस क्लास विनाशक घोषित किए बिना, नीचे दिए गए इस कार्यक्रम पर विचार करें; बिल्ली के लिए स्मृति साफ नहीं किया जा सकता है।

class Animal {
    public:
    ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat() {
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

आउटपुट:

Deleting an Animal
class Animal {
    public:
    virtual ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat(){
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

आउटपुट:

Deleting an Animal name Cat
Deleting an Animal

कीवर्ड आभासी संकलक को बताता है कि इसे प्रारंभिक बाध्यकारी नहीं करना चाहिए। इसके बजाए, इसे स्वचालित रूप से देर से बाध्यकारी करने के लिए आवश्यक सभी तंत्र स्थापित करना चाहिए। इसे पूरा करने के लिए, सामान्य कंपाइलर 1 वर्चुअल फ़ंक्शंस वाले प्रत्येक वर्ग के लिए एक एकल तालिका (जिसे VTABLE कहा जाता है) बनाता है। कंपाइलर VTABLE में उस विशेष श्रेणी के लिए वर्चुअल फ़ंक्शंस के पते रखता है। वर्चुअल फ़ंक्शंस वाले प्रत्येक वर्ग में, यह गुप्त रूप से एक पॉइंटर रखता है, जिसे vpointer (जिसे VPTR के रूप में संक्षिप्त किया जाता है) कहा जाता है, जो उस ऑब्जेक्ट के लिए VTABLE को इंगित करता है। जब आप बेस-क्लास पॉइंटर के माध्यम से वर्चुअल फ़ंक्शन कॉल करते हैं तो संकलक चुपचाप वीपीटीआर लाने के लिए कोड डालता है और VTABLE में फ़ंक्शन पता देखता है, इस प्रकार सही फ़ंक्शन को कॉल करता है और देर से बाध्यकारी होता है।

इस लिंक में अधिक जानकारी http://cplusplusinterviews.blogspot.sg/2015/04/virtual-mechanism.html


दक्षता के बारे में, वर्चुअल फ़ंक्शन प्रारंभिक बाध्यकारी फ़ंक्शंस के रूप में थोड़ा कम कुशल होते हैं।

"यह वर्चुअल कॉल तंत्र लगभग" सामान्य फ़ंक्शन कॉल "तंत्र (25% के भीतर) के रूप में लगभग कुशल हो सकता है। इसका स्पेस ओवरहेड वर्चुअल फ़ंक्शंस के साथ कक्षा के प्रत्येक ऑब्जेक्ट में एक पॉइंटर है और प्रत्येक वर्ग के लिए एक vtbl" [ Bjarne Stroustrup द्वारा सी ++ का दौरा ]


Here is complete example that illustrates why virtual method is used.

#include <iostream>

using namespace std;

class Basic
{
    public:
    virtual void Test1()
    {
        cout << "Test1 from Basic." << endl;
    }
    virtual ~Basic(){};
};
class VariantA : public Basic
{
    public:
    void Test1()
    {
        cout << "Test1 from VariantA." << endl;
    }
};
class VariantB : public Basic
{
    public:
    void Test1()
    {
        cout << "Test1 from VariantB." << endl;
    }
};

int main()
{
    Basic *object;
    VariantA *vobjectA = new VariantA();
    VariantB *vobjectB = new VariantB();

    object=(Basic *) vobjectA;
    object->Test1();

    object=(Basic *) vobjectB;
    object->Test1();

    delete vobjectA;
    delete vobjectB;
    return 0;
}

हमें आभासी कार्यों की आवश्यकता क्यों है?

वर्चुअल फ़ंक्शंस अनावश्यक टाइपकास्टिंग समस्या से बचते हैं, और हम में से कुछ बहस कर सकते हैं कि हमें वर्चुअल फ़ंक्शंस की आवश्यकता क्यों होती है जब हम व्युत्पन्न कक्षा में निर्दिष्ट फ़ंक्शन को कॉल करने के लिए व्युत्पन्न क्लास पॉइंटर का उपयोग कर सकते हैं! उत्तर है - यह बड़ी प्रणाली में विरासत के पूरे विचार को समाप्त करता है विकास, जहां सिंगल पॉइंटर बेस क्लास ऑब्जेक्ट होना बहुत वांछित है।

वर्चुअल फ़ंक्शंस के महत्व को समझने के लिए आइए दो सरल प्रोग्रामों की तुलना करें:

आभासी कार्यों के बिना कार्यक्रम:

#include <iostream>
using namespace std;

class father
{
    public: void get_age() {cout << "Fathers age is 50 years" << endl;}
};

class son: public father
{
    public : void get_age() { cout << "son`s age is 26 years" << endl;}
};

int main(){
    father *p_father = new father;
    son *p_son = new son;

    p_father->get_age();
    p_father = p_son;
    p_father->get_age();
    p_son->get_age();
    return 0;
}

उत्पादन:

Fathers age is 50 years
Fathers age is 50 years
son`s age is 26 years

वर्चुअल फ़ंक्शन के साथ प्रोग्राम:

#include <iostream>
using namespace std;

class father
{
    public:
        virtual void get_age() {cout << "Fathers age is 50 years" << endl;}
};

class son: public father
{
    public : void get_age() { cout << "son`s age is 26 years" << endl;}
};

int main(){
    father *p_father = new father;
    son *p_son = new son;

    p_father->get_age();
    p_father = p_son;
    p_father->get_age();
    p_son->get_age();
    return 0;
}

उत्पादन:

Fathers age is 50 years
son`s age is 26 years
son`s age is 26 years

दोनों आउटपुट का बारीकी से विश्लेषण करके, वर्चुअल फ़ंक्शंस के महत्व को समझ सकते हैं।


रन टाइम पॉलीमोर्फिज्म का समर्थन करने के लिए वर्चुअल फ़ंक्शंस का उपयोग किया जाता है।

यही है, वर्चुअल कीवर्ड कंपाइलर को संकलन समय पर निर्णय (फ़ंक्शन बाध्यकारी) नहीं करने के लिए कहता है , बल्कि रन टाइम के लिए इसे स्थगित करता है "

  • आप कीवर्ड virtual से पहले virtual घोषणा में virtual वर्चुअल कर सकते हैं। उदाहरण के लिए,

     class Base
     {
        virtual void func();
     }
    
  • जब बेस क्लास में आभासी सदस्य फ़ंक्शन होता है, तो बेस क्लास से प्राप्त होने वाली कोई भी कक्षा फ़ंक्शन को उसी प्रोटोटाइप के साथ फिर से परिभाषित कर सकती है यानी कार्यक्षमता को फिर से परिभाषित किया जा सकता है, फ़ंक्शन का इंटरफ़ेस नहीं।

     class Derive : public Base
     {
        void func();
     }
    
  • बेस क्लास पॉइंटर का उपयोग बेस क्लास ऑब्जेक्ट के साथ-साथ व्युत्पन्न क्लास ऑब्जेक्ट को इंगित करने के लिए किया जा सकता है।

  • जब वर्चुअल फ़ंक्शन को बेस क्लास पॉइंटर का उपयोग करके बुलाया जाता है, तो संकलक रन-टाइम पर फ़ंक्शन का संस्करण निर्धारित करता है - यानी बेस क्लास संस्करण या ओवरराइड व्युत्पन्न क्लास संस्करण - जिसे कॉल किया जाना है। इसे रन टाइम पॉलिमॉर्फिज्म कहा जाता है।

यहां बताया गया है कि मैं न केवल virtual फ़ंक्शंस को समझता हूं, लेकिन उन्हें क्यों आवश्यकता है:

मान लें कि आपके पास ये दो वर्ग हैं:

class Animal
{
    public:
        void eat() { std::cout << "I'm eating generic food."; }
};

class Cat : public Animal
{
    public:
        void eat() { std::cout << "I'm eating a rat."; }
};

आपके मुख्य समारोह में:

Animal *animal = new Animal;
Cat *cat = new Cat;

animal->eat(); // Outputs: "I'm eating generic food."
cat->eat();    // Outputs: "I'm eating a rat."

अभी तक बहुत अच्छा है, ठीक है ना? पशु सामान्य भोजन खाते हैं, बिल्लियों चूहों खाते हैं, बिना virtual

आइए अब इसे थोड़ा बदल दें ताकि eat() को मध्यवर्ती फ़ंक्शन के माध्यम से बुलाया जा सके (केवल इस उदाहरण के लिए एक छोटा कार्य):

// This can go at the top of the main.cpp file
void func(Animal *xyz) { xyz->eat(); }

अब हमारा मुख्य कार्य है:

Animal *animal = new Animal;
Cat *cat = new Cat;

func(animal); // Outputs: "I'm eating generic food."
func(cat);    // Outputs: "I'm eating generic food."

ओह ओह ... हमने एक बिल्ली को func() में पारित किया, लेकिन यह चूहे नहीं खाएगा। क्या आपको func() अधिभारित करना चाहिए, इसलिए यह Cat* लेता है? यदि आपको पशु से अधिक जानवरों को प्राप्त करना है तो उन्हें सभी को अपने func() आवश्यकता होगी।

समाधान Animal वर्ग से वर्चुअल फ़ंक्शन eat() को eat() लिए है:

class Animal
{
    public:
        virtual void eat() { std::cout << "I'm eating generic food."; }
};

class Cat : public Animal
{
    public:
        void eat() { std::cout << "I'm eating a rat."; }
};

मुख्य:

func(animal); // Outputs: "I'm eating generic food."
func(cat);    // Outputs: "I'm eating a rat."

किया हुआ।


We need virtual methods for supporting "Run time Polymorphism". When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.


आपको ओवरराइडिंग और ओवरलोडिंग के बीच अंतर करना होगा। virtual कीवर्ड के बिना आप केवल बेस क्लास की एक विधि अधिभारित करते हैं। इसका मतलब छिपाने के अलावा कुछ भी नहीं है। आइए मान लें कि आपके पास बेस क्लास Base और व्युत्पन्न क्लास Specialized जो दोनों void foo() कार्यान्वित करते हैं। अब आपके पास Specialized उदाहरण के लिए इंगित करने के लिए Base लिए एक सूचक है। जब आप foo() पर कॉल करते हैं तो आप virtual बनाता है: यदि विधि आभासी है, तो Specialized से कार्यान्वयन का उपयोग किया जाएगा, यदि यह अनुपलब्ध है, तो Base से संस्करण चुना जाएगा। बेस क्लास से अधिभार विधियों को कभी भी नहीं करना सबसे अच्छा अभ्यास है। गैर-वर्चुअल विधि बनाना लेखक के बारे में आपको यह बताना है कि सबक्लास में इसका विस्तार इरादा नहीं है।


आपको कम से कम 1 स्तर की विरासत और इसे प्रदर्शित करने के लिए एक डाउनकास्ट की आवश्यकता है। यहां एक बहुत ही सरल उदाहरण है:

class Animal
{        
    public: 
      // turn the following virtual modifier on/off to see what happens
      //virtual   
      std::string Says() { return "?"; }  
};

class Dog: public Animal
{
    public: std::string Says() { return "Woof"; }
};

void test()
{
    Dog* d = new Dog();
    Animal* a = d;       // refer to Dog instance with Animal pointer

    cout << d->Says();   // always Woof
    cout << a->Says();   // Woof or ?, depends on virtual
}

आभासी कीवर्ड संकलक को पॉइंटर वर्ग की बजाय ऑब्जेक्ट की कक्षा में परिभाषित विधि कार्यान्वयन को चुनने के लिए मजबूर करता है।

Shape *shape = new Triangle(); 
cout << shape->getName();

उपर्युक्त उदाहरण में, आकार :: getName डिफ़ॉल्ट रूप से कॉल किया जाएगा, जब तक getName () को बेस क्लास आकार में वर्चुअल के रूप में परिभाषित नहीं किया जाता है। यह कंपाइलर को आकार वर्ग के बजाय त्रिकोण वर्ग में getName () कार्यान्वयन की तलाश करने के लिए मजबूर करता है।

आभासी तालिका वह तंत्र है जिसमें संकलक उप-वर्गों के विभिन्न वर्चुअल-विधि कार्यान्वयन का ट्रैक रखता है। इसे गतिशील प्रेषण भी कहा जाता है, और इसके साथ जुड़े कुछ ओवरहेड भी हैं।

आखिरकार, वर्चुअल को सी ++ में क्यों जरूरी है, क्यों इसे जावा में डिफ़ॉल्ट व्यवहार नहीं बनाते?

  1. सी ++ "शून्य ओवरहेड" और "आप जो भी उपयोग करते हैं उसके लिए भुगतान करें" के सिद्धांतों पर आधारित है। तो यह आपके लिए गतिशील प्रेषण करने की कोशिश नहीं करता है, जब तक कि आपको इसकी आवश्यकता न हो।
  2. इंटरफेस पर अधिक नियंत्रण प्रदान करने के लिए। फ़ंक्शन गैर वर्चुअल बनाकर, इंटरफ़ेस / अमूर्त वर्ग अपने सभी कार्यान्वयन में व्यवहार को नियंत्रित कर सकता है।

इंटरफेस डिजाइन में वर्चुअल विधियों का उपयोग किया जाता है। उदाहरण के लिए विंडोज़ में एक इंटरफेस है जिसे IU अज्ञात कहा जाता है:

interface IUnknown {
  virtual HRESULT QueryInterface (REFIID riid, void **ppvObject) = 0;
  virtual ULONG   AddRef () = 0;
  virtual ULONG   Release () = 0;
};

कार्यान्वयन के लिए इंटरफ़ेस उपयोगकर्ता को ये विधियां छोड़ी गई हैं। वे कुछ वस्तुओं के निर्माण और विनाश के लिए आवश्यक हैं जिन्हें अनजान होना चाहिए। इस मामले में रन-टाइम तीन तरीकों से अवगत है और उन्हें कॉल करने पर उन्हें लागू करने की उम्मीद है। तो एक अर्थ में वे वस्तु के बीच एक अनुबंध के रूप में कार्य करते हैं और जो भी वस्तु उस वस्तु का उपयोग करता है।


मेरे पास एक बेहतर बातचीत के लिए वार्तालाप के रूप में मेरा जवाब है:

हमें आभासी कार्यों की आवश्यकता क्यों है?

पॉलिमॉर्फिज्म के कारण।

पॉलिमॉर्फिज्म क्या है?

तथ्य यह है कि आधार सूचक भी व्युत्पन्न प्रकार वस्तुओं को इंगित कर सकता है।

पॉलिमॉर्फिज्म की यह परिभाषा वर्चुअल फ़ंक्शंस की आवश्यकता में कैसे जाती है?

खैर, शुरुआती बाध्यकारी के माध्यम से।

प्रारंभिक बाध्यकारी क्या है?

सी ++ में प्रारंभिक बाध्यकारी (संकलन-समय बाध्यकारी) का अर्थ है कि प्रोग्राम निष्पादित होने से पहले एक फ़ंक्शन कॉल तय किया जाता है।

इसलिए...?

इसलिए यदि आप किसी फ़ंक्शन के पैरामीटर के रूप में बेस प्रकार का उपयोग करते हैं, तो कंपाइलर केवल बेस इंटरफ़ेस को पहचान लेगा, और यदि आप व्युत्पन्न कक्षाओं से किसी भी तर्क के साथ उस फ़ंक्शन को कॉल करते हैं, तो यह कटा हुआ हो जाता है, जो आप नहीं करना चाहते हैं।

यदि ऐसा नहीं है कि हम क्या करना चाहते हैं, तो इसकी अनुमति क्यों है?

क्योंकि हमें पॉलिमॉर्फिज्म चाहिए!

तब पॉलिमॉर्फिज्म का क्या फायदा है?

आप किसी एकल फ़ंक्शन के पैरामीटर के रूप में बेस टाइप पॉइंटर का उपयोग कर सकते हैं, और उसके बाद अपने प्रोग्राम के रन-टाइम में, आप किसी भी समस्या के बिना व्युत्पन्न प्रकार इंटरफ़ेस (जैसे उनके सदस्य फ़ंक्शंस) तक पहुंच सकते हैं, उस सिंगल के डिफ्रेंसिंग का उपयोग करके आधार सूचक

मुझे अभी भी पता नहीं है कि वर्चुअल फ़ंक्शन क्या अच्छे हैं ...! और यह मेरा पहला सवाल था!

अच्छा, ऐसा इसलिए है क्योंकि आपने जल्द ही अपने प्रश्न पूछा!

हमें आभासी कार्यों की आवश्यकता क्यों है?

मान लें कि आपने बेस पॉइंटर के साथ एक फ़ंक्शन कहा है, जिसमें किसी ऑब्जेक्ट का पता उसके व्युत्पन्न वर्गों में से एक है। जैसा कि हमने उपरोक्त के बारे में बात की है, रन-टाइम में, इस सूचक को संदर्भित किया जाता है, अब तक इतना अच्छा है, हालांकि, हम एक विधि (== एक सदस्य फ़ंक्शन) "हमारे व्युत्पन्न वर्ग से" निष्पादित होने की अपेक्षा करते हैं! हालांकि, एक ही विधि (जिसकी एक ही शीर्षलेख है) पहले से ही बेस क्लास में परिभाषित है, तो आपका प्रोग्राम अन्य विधि चुनने के लिए परेशान क्यों होना चाहिए? दूसरे शब्दों में मेरा मतलब है, आप इस परिदृश्य को सामान्य रूप से क्या देखते थे, उससे पहले आप कैसे कह सकते हैं?

संक्षिप्त उत्तर "बेस में वर्चुअल सदस्य फ़ंक्शन" है, और थोड़ा लंबा जवाब यह है कि, "इस चरण में, यदि प्रोग्राम बेस क्लास में वर्चुअल फ़ंक्शन देखता है, तो यह जानता है (एहसास) कि आप उपयोग करने का प्रयास कर रहे हैं polymorphism "और इसी तरह एक ही शीर्षलेख के साथ एक और विधि खोजने के लिए व्युत्पन्न कक्षाओं ( v-table , देर से बाध्यकारी का एक रूप) का उपयोग करने के लिए व्युत्पन्न कक्षाओं में जाता है, लेकिन - अप्रत्याशित रूप से - एक अलग कार्यान्वयन।

एक अलग कार्यान्वयन क्यों?

तुम नाक-सिर! एक अच्छी किताब पढ़ें!

ठीक है, प्रतीक्षा प्रतीक्षा करें, बेस बेसर्स का उपयोग करने के लिए परेशान क्यों होगा, जब वह व्युत्पन्न प्रकार पॉइंटर्स का उपयोग कर सकता है? आप जज हो, क्या यह सब सिरदर्द इसके लायक है? इन दो स्निपेट को देखो:

// 1:

Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();

// 2:

Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();

ठीक है, हालांकि मुझे लगता है कि 1 अभी भी 2 से बेहतर है, आप इस तरह 1 लिख सकते हैं:

// 1:

Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();

और इसके अलावा, आपको अवगत होना चाहिए कि यह अभी तक आपके द्वारा समझाई गई सभी चीजों का एक संक्षिप्त उपयोग है। इसके बजाए, उदाहरण के लिए मान लीजिए जिसमें आपके प्रोग्राम में एक फ़ंक्शन था जो क्रमशः प्रत्येक व्युत्पन्न कक्षाओं के तरीकों का उपयोग करता था (getMonthBenefit ()):

double totalMonthBenefit = 0;    
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
     totalMonthBenefit += x -> getMonthBenefit();
}

अब, किसी भी सिरदर्द के बिना , इसे फिर से लिखने का प्रयास करें !

double totalMonthBenefit=0;
Shop1* branch1 = &shop1;
Shop2* branch2 = &shop2;
Shop3* branch3 = &shop3;
Shop4* branch4 = &shop4;
Shop5* branch5 = &shop5;
Shop6* branch6 = &shop6;
totalMonthBenefit += branch1 -> getMonthBenefit();
totalMonthBenefit += branch2 -> getMonthBenefit();
totalMonthBenefit += branch3 -> getMonthBenefit();
totalMonthBenefit += branch4 -> getMonthBenefit();
totalMonthBenefit += branch5 -> getMonthBenefit();
totalMonthBenefit += branch6 -> getMonthBenefit();

और वास्तव में, यह अभी तक एक विकसित उदाहरण हो सकता है!







virtual-functions