क्या मुझे आधार वर्चुअल डिस्ट्रक्टर को स्पष्ट रूप से कॉल करने की आवश्यकता है?


351

C ++ में क्लास को ओवरराइड करते समय (वर्चुअल डिस्ट्रक्टर के साथ) मैं डिस्ट्रॉक्टर को फिर से इनहेरिटिंग क्लास पर वर्चुअल के रूप में लागू कर रहा हूं, लेकिन क्या मुझे बेस डिस्ट्रक्टर को कॉल करने की आवश्यकता है?

यदि हां, तो मुझे लगता है कि यह कुछ इस तरह है ...

MyChildClass::~MyChildClass() // virtual in header
{
    // Call to base destructor...
    this->MyBaseClass::~MyBaseClass();

    // Some destructing specific to MyChildClass
}

क्या मैं सही हू?

जवाबों:


472

नहीं, निर्माण के रिवर्स ऑर्डर में विनाशकों को स्वचालित रूप से कहा जाता है। (बेस कक्षाएं अंतिम)। बेस क्लास डिस्ट्रक्टर्स को न बुलाएं।


शुद्ध आभासी विध्वंसक के बारे में क्या? मेरा लिंकर मेरे विरासत वाले वर्ग के गैर-आभासी विध्वंसक के अंत में इसे कॉल करने की कोशिश कर रहा है;
cjcurrie

40
आपके पास शरीर के बिना एक शुद्ध आभासी विध्वंसक नहीं हो सकता। बस इसे एक खाली शरीर दें। एक नियमित शुद्ध आभासी विधि के साथ, ओवरराइडिंग फ़ंक्शन को इसके बजाय कहा जाता है, विध्वंसक के साथ, वे सभी कहलाते हैं, इसलिए आपको एक शरीर प्रदान करना होगा। = 0 का तात्पर्य यह है कि इसे ओवरराइड किया जाना चाहिए, इसलिए यदि आवश्यक हो तो अभी भी एक उपयोगी निर्माण।
लू फ्रेंको


निक बोल्टन का कोड एक विभाजन दोष का कारण क्यों नहीं बनता है, हालांकि यह बेस डिस्ट्रक्टर को दो बार कॉल करता है, जबकि deleteएक पॉइंटर को बेस क्लास पर दो बार कॉल करने से एक विभाजन गलती होती है?
मैगीयरो

2
आपको किसी भी गलत कोड के साथ विभाजन की गलती की गारंटी नहीं है। इसके अलावा, एक विध्वंसक को कॉल करने से मेमोरी जारी नहीं होती है।
लो फ्रेंको

92

नहीं, आपको बेस डिस्ट्रक्टर को कॉल करने की आवश्यकता नहीं है, एक बेस डिस्ट्रक्टर हमेशा आपके लिए व्युत्पन्न डिस्ट्रक्टर द्वारा कहा जाता है। कृपया विनाश से संबंधित आदेश के लिए मेरा संबंधित जवाब यहां देखें

यह समझने के लिए कि आप बेस क्लास में वर्चुअल डिस्ट्रक्टर क्यों चाहते हैं, कृपया नीचे दिया गया कोड देखें:

class B
{
public:
    virtual ~B()
    {
        cout<<"B destructor"<<endl;
    }
};


class D : public B
{
public:
    virtual ~D()
    {
        cout<<"D destructor"<<endl;
    }
};

जब तुम करोगे:

B *pD = new D();
delete pD;

तब यदि आपके पास B में वर्चुअल डिस्ट्रक्टर नहीं है, तो केवल ~ B () कहा जाएगा। लेकिन जब से आपके पास एक आभासी विध्वंसक है, पहले ~ डी () को बुलाया जाएगा, फिर ~ बी ()।


20
कृपया प्रोग्राम (छद्म) आउटपुट शामिल करें। यह पाठक की मदद करेगा।
कुलदीप सिंह ढाका

@KuldeepSinghDhaka पाठक इसे wandbox.org/permlink/KQtbZG1hjVgceSlO पर लाइव देख सकते हैं ।
सूअर

27

दूसरों ने क्या कहा, लेकिन यह भी ध्यान दें कि आपको व्युत्पन्न वर्ग में विध्वंसक आभासी घोषित करने की आवश्यकता नहीं है। एक बार जब आप एक विध्वंसक आभासी घोषित करते हैं, जैसा कि आप आधार वर्ग में करते हैं, सभी व्युत्पन्न विध्वंसक आभासी होंगे चाहे आप उन्हें घोषित करते हैं या नहीं। दूसरे शब्दों में:

struct A {
   virtual ~A() {}
};

struct B : public A {
   virtual ~B() {}   // this is virtual
};

struct C : public A {
   ~C() {}          // this is virtual too
};

1
क्या होगा अगर ~ B को वर्चुअल घोषित नहीं किया जाता है? क्या ~ C अभी भी आभासी है?
विल

5
हाँ। जब एक आभासी विधि (कोई भी, केवल विध्वंसक नहीं) को आभासी घोषित किया जाता है, तो व्युत्पन्न कक्षाओं में उस पद्धति के सभी ओवरराइड स्वचालित रूप से आभासी होते हैं। इस स्थिति में, भले ही आप ~ B आभासी घोषित न करें, फिर भी यह है, और इसलिए ~ C है।
बॉयसी

1
लेकिन आधार वर्ग में उनके संबंधित तरीकों के समान नाम और मापदंडों वाले अन्य ओवरराइड तरीकों के विपरीत, विध्वंसक नाम अलग है। क्या यह अलग बात है? @boycy
युआन वेन

1
@YuanWen नहीं यह नहीं होगा, (एक और केवल) व्युत्पन्न विध्वंसक हमेशा अपने आधार वर्ग (एक और केवल) विध्वंसक को ओवरराइड करता है।
ब्वॉयसी

10

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


9

नहीं, आप बेस क्लास डिस्ट्रक्टर को कभी नहीं कहते हैं, इसे हमेशा स्वचालित रूप से कहा जाता है जैसे कि दूसरों ने इंगित किया है लेकिन यहां परिणाम के साथ अवधारणा का प्रमाण है:

class base {
public:
    base()  { cout << __FUNCTION__ << endl; }
    ~base() { cout << __FUNCTION__ << endl; }
};

class derived : public base {
public:
    derived() { cout << __FUNCTION__ << endl; }
    ~derived() { cout << __FUNCTION__ << endl; } // adding call to base::~base() here results in double call to base destructor
};


int main()
{
    cout << "case 1, declared as local variable on stack" << endl << endl;
    {
        derived d1;
    }

    cout << endl << endl;

    cout << "case 2, created using new, assigned to derive class" << endl << endl;
    derived * d2 = new derived;
    delete d2;

    cout << endl << endl;

    cout << "case 3, created with new, assigned to base class" << endl << endl;
    base * d3 = new derived;
    delete d3;

    cout << endl;

    return 0;
}

आउटपुट है:

case 1, declared as local variable on stack

base::base
derived::derived
derived::~derived
base::~base


case 2, created using new, assigned to derive class

base::base
derived::derived
derived::~derived
base::~base


case 3, created with new, assigned to base class

base::base
derived::derived
base::~base

Press any key to continue . . .

यदि आप बेस क्लास डिस्ट्रक्टर को वर्चुअल के रूप में सेट करते हैं जो कि एक होना चाहिए, तो केस 3 के परिणाम केस 1 और 2 के समान होंगे।


अच्छा चित्रण। यदि आप व्युत्पन्न वर्ग से बेस क्लास डिस्ट्रक्टर को कॉल करने का प्रयास करते हैं, तो आपको "त्रुटि: इसी प्रकार की कॉल" के लिए कोई मिलान फ़ंक्शन नहीं मिलना चाहिए, जैसे 'आधार :: बेस ()' <newline> ~ आधार (); " कम से कम मेरे g ++ 7.x कंपाइलर से यही व्यवहार है।
केमिन झोउ


1

सी ++ में विध्वंसक अपने निर्माणों के क्रम में स्वचालित रूप से कॉल किया जाता है (व्युत्पन्न तब आधार) केवल तब होता है जब बेस क्लास विध्वंसक घोषित किया जाता हैvirtual

यदि नहीं, तो ऑब्जेक्ट विलोपन के समय केवल बेस क्लास डिस्ट्रक्टर को लागू किया जाता है।

उदाहरण: वर्चुअल डिस्ट्रक्टर के बिना

#include <iostream>

using namespace std;

class Base{
public:
  Base(){
    cout << "Base Constructor \n";
  }

  ~Base(){
    cout << "Base Destructor \n";
  }

};

class Derived: public Base{
public:
  int *n;
  Derived(){
    cout << "Derived Constructor \n";
    n = new int(10);
  }

  void display(){
    cout<< "Value: "<< *n << endl;
  }

  ~Derived(){
    cout << "Derived Destructor \n";
  }
};

int main() {

 Base *obj = new Derived();  //Derived object with base pointer
 delete(obj);   //Deleting object
 return 0;

}

उत्पादन

Base Constructor
Derived Constructor
Base Destructor

उदाहरण: बेस वर्चुअल डिस्ट्रक्टर के साथ

#include <iostream>

using namespace std;

class Base{
public:
  Base(){
    cout << "Base Constructor \n";
  }

  //virtual destructor
  virtual ~Base(){
    cout << "Base Destructor \n";
  }

};

class Derived: public Base{
public:
  int *n;
  Derived(){
    cout << "Derived Constructor \n";
    n = new int(10);
  }

  void display(){
    cout<< "Value: "<< *n << endl;
  }

  ~Derived(){
    cout << "Derived Destructor \n";
    delete(n);  //deleting the memory used by pointer
  }
};

int main() {

 Base *obj = new Derived();  //Derived object with base pointer
 delete(obj);   //Deleting object
 return 0;

}

उत्पादन

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

बेस क्लास डिस्ट्रक्टर को घोषित करने की सिफारिश की जाती है virtual, अन्यथा यह अपरिभाषित व्यवहार का कारण बनता है।

संदर्भ: आभासी विध्वंसक

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.