क्या एक पॉइंटर को एक सबक्लास पर डिलीट करने से बेस क्लास डिस्ट्रक्टर होता है?


165

मेरे पास class Aइसके एक क्षेत्र के लिए ढेर स्मृति आवंटन का उपयोग है। क्लास ए को त्वरित किया जाता है और एक अन्य कक्षा में पॉइंटर फ़ील्ड के रूप में संग्रहीत किया जाता है ( class B

जब मुझे कक्षा बी की एक वस्तु के साथ किया जाता है, तो मैं कॉल deleteकरता हूं, जो मुझे लगता है कि विध्वंसक को कॉल करता है ... लेकिन क्या यह कक्षा ए के विनाशकर्ता को भी बुलाता है?

संपादित करें:

उत्तरों से, मैं इसे लेता हूं (कृपया गलत होने पर संपादित करें):

  1. delete बी कॉल की एक उदाहरण बी :: ~ बी ();
  2. जो कहता है A::~A();
  3. A::~A स्पष्ट रूप deleteसे ए वस्तु के सभी ढेर-आवंटित सदस्य चर चाहिए ;
  4. अंत में मेमोरी ब्लॉक स्टोरिंग ने कहा कि वर्ग बी का उदाहरण ढेर पर वापस आ गया है - जब नया प्रयोग किया गया था, तो उसने पहले हीप पर मेमोरी का एक ब्लॉक आवंटित किया था, फिर इसे शुरू करने के लिए बिल्डरों को आमंत्रित किया, अब सभी विध्वंसक के बाद वस्तु को अंतिम रूप देने के लिए आमंत्रित किया गया है ब्लॉक जहां ऑब्जेक्ट रहता है, उसे ढेर पर लौटाया जाता है।

जवाबों:


183

A का विनाशक तब चलेगा जब उसका जीवनकाल समाप्त हो जाएगा। यदि आप चाहते हैं कि इसकी मेमोरी फ़्री हो जाए और डिस्ट्रक्टर रन हो जाए, तो आपको इसे डिलीट करना होगा यदि इसे हीप पर आबंटित किया गया था। यदि इसे स्टैक पर आवंटित किया गया था तो यह स्वचालित रूप से होता है (यानी जब यह दायरे से बाहर हो जाता है; RAII देखें)। यदि यह एक वर्ग का सदस्य है (सूचक नहीं, बल्कि पूर्ण सदस्य है), तो यह तब होगा जब युक्त वस्तु नष्ट हो जाएगी।

class A
{
    char *someHeapMemory;
public:
    A() : someHeapMemory(new char[1000]) {}
    ~A() { delete[] someHeapMemory; }
};

class B
{
    A* APtr;
public:
    B() : APtr(new A()) {}
    ~B() { delete APtr; }
};

class C
{
    A Amember;
public:
    C() : Amember() {}
    ~C() {} // A is freed / destructed automatically.
};

int main()
{
    B* BPtr = new B();
    delete BPtr; // Calls ~B() which calls ~A() 
    C *CPtr = new C();
    delete CPtr;
    B b;
    C c;
} // b and c are freed/destructed automatically

उपर्युक्त उदाहरण में, प्रत्येक हटाना और हटाना [] आवश्यक है। और किसी भी डिलीट की जरूरत नहीं है (या वास्तव में इस्तेमाल किया जा सकता है) जहां मैंने इसका इस्तेमाल नहीं किया।

auto_ptr, unique_ptrऔर shared_ptrआदि ... इस जीवनकाल प्रबंधन को बहुत आसान बनाने के लिए महान हैं:

class A
{
    shared_array<char> someHeapMemory;
public:
    A() : someHeapMemory(new char[1000]) {}
    ~A() { } // someHeapMemory is delete[]d automatically
};

class B
{
    shared_ptr<A> APtr;
public:
    B() : APtr(new A()) {}
    ~B() {  } // APtr is deleted automatically
};

int main()
{
    shared_ptr<B> BPtr = new B();
} // BPtr is deleted automatically

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

सूचक सिर्फ एक संख्या है। तुम भी गलती से ++उस पर ऑपरेटर का उपयोग कर सकते हैं । इसलिए मुझे आश्चर्य होता है कि पॉइंटर जो कि वर्ग डेटा के बीच के बिंदुओं पर अभी भी प्रभाव डालता है।
टॉम ज़ातो -

2
@ TomášZato: यदि आप एक यादृच्छिक सूचक पर डिलीट कहते हैं, तो आप खराब हो गए हैं। ऐसा करने का कोई अच्छा कारण नहीं है। वास्तव में, यदि आप स्मार्ट-पॉइंटर डिस्ट्रक्टर के अलावा कहीं भी मैन्युअल रूप से डिलीट कॉल कर रहे हैं, तो आप शायद इस बात का दूसरा रूप लेना चाहते हैं कि आप स्मार्ट पॉइंटर या किसी अन्य ऑब्जेक्ट मैनेजर का उपयोग क्यों नहीं कर रहे हैं।
ग्रहण

share_array केवल बूस्ट से है, हाँ?
द्रोणज

30

जब आप नए द्वारा आवंटित पॉइंटर पर डिलीट कहते हैं, तो इंगित की गई वस्तु का विनाशकर्ता कहा जाएगा।

A * p = new A;

delete p;    // A:~A() called for you on obkect pointed to by p

22

इसे "विध्वंसक" नाम दिया गया है, न कि "डिकंस्ट्रक्टर"।

प्रत्येक वर्ग के विध्वंसक के अंदर, आपको नए के साथ आवंटित किए गए अन्य सभी सदस्य चर को हटाना होगा।

संपादित करें: स्पष्ट करने के लिए:

बोलो तुम्हारे पास है

struct A {}

class B {
    A *a;
public:
    B () : a (new A) {}
    ~B() { delete a; }
};

class C {
    A *a;
public:
    C () : a (new A) {}        
};

int main () {
    delete new B;
    delete new C;
}

B का एक उदाहरण आवंटित करना और फिर हटाना साफ है, क्योंकि जो आंतरिक रूप से B आवंटित करता है, वह भी विध्वंसक में हटा दिया जाएगा।

लेकिन कक्षा सी के उदाहरण स्मृति को रिसाव करेंगे, क्योंकि यह ए का एक उदाहरण आवंटित करता है जो इसे जारी नहीं करता है (इस मामले में सी में एक विध्वंसक भी नहीं है)।


5

यदि आपके पास एक सामान्य सूचक ( A*) है, तो विध्वंसक को नहीं बुलाया जाएगा (और Aउदाहरण के लिए मेमोरी को या तो मुक्त नहीं किया जाएगा) जब तक कि आप deleteस्पष्ट रूप से Bविध्वंसक नहीं करते। यदि आप चाहते हैं कि स्वचालित विनाश स्मार्ट पॉइंटर्स को देखें auto_ptr


4

आपको बी के विध्वंसक में खुद को हटाना चाहिए।


4
class B
{
public:
    B()
    {
       p = new int[1024];  
    }
    virtual ~B()
    {
        cout<<"B destructor"<<endl;
        //p will not be deleted EVER unless you do it manually.
    }
    int *p;
};


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

जब तुम करोगे:

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

विध्वंसक को केवल तभी कहा जाएगा जब आपके आधार वर्ग में वर्चुअल कीवर्ड होगा।

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

जब तक आप स्पष्ट रूप से उन्हें नहीं हटाते, ढेर पर आवंटित बी या डी का कोई भी सदस्य नहीं होगा। और उन्हें हटाने से उनका विध्वंसक भी होगा।


1

आपके पास कुछ है

class B
{
   A * a;
}
B * b = new B;
b->a = new A;

यदि आप कॉल करते हैं delete b;, तो कुछ भी नहीं होता है, और आपके पास एक मेमोरी लीक है। याद रखने की कोशिश करना delete b->a;एक अच्छा समाधान नहीं है, लेकिन कुछ और भी हैं।

B::~B() {delete a;}

यह B के लिए एक विनाशकारी है जो एक को हटा देगा। (यदि कोई 0 है, तो वह डिलीट कुछ भी नहीं करता है। यदि a 0 नहीं है, लेकिन नए से मेमोरी को इंगित नहीं करता है, तो आपको ढेर भ्रष्टाचार मिलता है।)

auto_ptr<A> a;
...
b->a.reset(new A);

इस तरह आपके पास एक पॉइंटर के रूप में नहीं है, बल्कि एक auto_ptr <> (साझा_ptr <> भी, या अन्य स्मार्ट पॉइंटर्स के रूप में) करेगा, और b होने पर यह स्वचालित रूप से हटा दिया जाता है।

इन तरीकों में से कोई भी अच्छी तरह से काम करता है, और मैंने दोनों का उपयोग किया है।


1

मैं सोच रहा था कि मेरी कक्षा के विध्वंसक को क्यों नहीं बुलाया गया। कारण यह था कि मैं उस वर्ग की परिभाषा को शामिल करना भूल गया था (#include "class.h")। मेरे पास केवल "क्लास ए;" जैसी घोषणा थी। और कंपाइलर इससे खुश था और मुझे "डिलीट" करने के लिए कहा।


संकलक चेतावनी स्तर बढ़ाएँ
Phil1970

0

नहीं। सूचक हटा दिया जाएगा। आपको बी के विध्वंसक में ए पर स्पष्ट को हटाना चाहिए।


मैं यह कर रहा हूं, मेरा सवाल था कि विध्वंसक कहा जाता है?
निक बोल्टन

0

वर्ग A के ऑब्जेक्ट के लिए विध्वंसक को केवल तभी कहा जाएगा यदि डिलीट उस ऑब्जेक्ट के लिए कहा जाता है। कक्षा B के विध्वंसक में उस सूचक को हटाना सुनिश्चित करें।

किसी ऑब्जेक्ट पर डिलीट होने पर क्या होता है, इस बारे में थोड़ी और जानकारी के लिए देखें: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9


0

नहीं, यह क्लास ए के लिए विध्वंसक नहीं कहेगा, आपको इसे स्पष्ट रूप से कॉल करना चाहिए (जैसे पॉवरॉय ने बताया), लाइन को हटाएं 'ptr हटाएं;' तुलना करने के लिए उदाहरण में ...

  #include <iostream>

  class A
  {
     public:
        A(){};
        ~A();
  };

  A::~A()
  {
     std::cout << "Destructor of A" << std::endl;
  }

  class B
  {
     public:
        B(){ptr = new A();};
        ~B();
     private:
        A* ptr;
  };

  B::~B()
  {
     delete ptr;
     std::cout << "Destructor of B" << std::endl;
  }

  int main()
  {
     B* b = new B();
     delete b;
     return 0;
  }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.