هل يؤدي الحذف على مؤشر إلى فئة فرعية إلى استدعاء مدمر الفئة الأساسية؟

StackOverflow https://stackoverflow.com/questions/677653

سؤال

انا لدي class A الذي يستخدم تخصيص ذاكرة الكومة لأحد حقوله.يتم إنشاء مثيل للفئة A وتخزينها كحقل مؤشر في فئة أخرى (class B.

عندما أنتهي من كائن من الفئة ب، أتصل delete, ، والذي أفترض أنه يدعو المدمر ...ولكن هل هذا يستدعي مدمر الفئة (أ) أيضًا؟

يحرر:

من الإجابات أفهم ذلك (يرجى التعديل إذا كان غير صحيح):

  1. delete لمثيل B يستدعي B::~B();
  2. الذي يدعو A::~A();
  3. A::~A يجب صراحة delete كافة متغيرات الأعضاء المخصصة للكومة للكائن A؛
  4. أخيرًا، يتم إرجاع كتلة الذاكرة التي تخزن المثيل المذكور للفئة B إلى الكومة - متى جديد تم استخدامه، فقد خصص أولاً كتلة من الذاكرة على الكومة، ثم استدعى المنشئات لتهيئتها، والآن بعد استدعاء جميع المدمرات لإنهاء الكائن، يتم إرجاع الكتلة التي يوجد بها الكائن إلى الكومة.
هل كانت مفيدة؟

المحلول

سيتم تشغيل المدمر لـ A عندما ينتهي عمره.إذا كنت تريد تحرير ذاكرتها وتشغيل أداة التدمير، فيجب عليك حذفها إذا تم تخصيصها في الكومة.إذا تم تخصيصه على المكدس، فسيحدث هذا تلقائيًا (على سبيل المثال.عندما يخرج عن النطاق؛انظر راي).إذا كان عضوًا في فئة (ليس مؤشرًا، بل عضوًا كاملاً)، فسيحدث هذا عند تدمير الكائن المحتوي.

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

نصائح أخرى

عند استدعاء حذف على مؤشر مخصص بواسطة جديد، سيتم استدعاء مدمر الكائن المشار إليه.

A * p = new A;

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

يطلق عليه اسم "المدمر" وليس "المفكك".

داخل أداة التدمير لكل فئة، يجب عليك حذف كافة متغيرات الأعضاء الأخرى التي تم تخصيصها مع new.

يحرر:للتوضيح:

قل أن لديك

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 داخليًا سيتم حذفه أيضًا في أداة التدمير.

لكن مثيلات الفئة C سوف تتسرب من الذاكرة، لأنها تخصص مثيل A الذي لا تحرره (في هذه الحالة، C لا تحتوي حتى على أداة إتلاف).

إذا كان لديك مؤشر عادي (A*) فلن يتم استدعاء المدمر (والذاكرة لـ A لن يتم تحرير المثيل أيضًا) إلا إذا قمت بذلك delete صراحة في Bالمدمرة.إذا كنت تريد التدمير التلقائي، فانظر إلى المؤشرات الذكية مثل auto_ptr.

يجب عليك حذف A بنفسك في أداة تدمير B.

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;

سيتم استدعاء المدمر فقط إذا كانت فئتك الأساسية تحتوي على الكلمة الأساسية الافتراضية.

ثم إذا لم يكن لديك أداة تدمير افتراضية، فسيتم استدعاء ~B() فقط.ولكن بما أن لديك أداة تدمير افتراضية، فسيتم استدعاء ~D() أولاً، ثم ~B().

لن يتم إلغاء تخصيص أي أعضاء من B أو D المخصصين في الكومة إلا إذا قمت بحذفهم بشكل صريح.وسيؤدي حذفها إلى استدعاء المدمر أيضًا.

كنت أتساءل لماذا لم يتم استدعاء مدمر صفي.والسبب هو أنني نسيت تضمين تعريف تلك الفئة (#include "class.h").لم يكن لدي سوى إعلان مثل "الفئة أ" ؛ وكان المترجم سعيدًا به ودعني أسمي "حذف".

لا.سيتم حذف المؤشر.يجب عليك استدعاء الحذف على A بشكل صريح في أداة تدمير B.

سيتم استدعاء المدمر لكائن الفئة A فقط إذا تم استدعاء الحذف لهذا الكائن.تأكد من حذف هذا المؤشر في أداة تدمير الفئة B.

لمزيد من المعلومات حول ما يحدث عند استدعاء الحذف على كائن، راجع:http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9

لا ، لن يدعو Destructor للفئة A ، يجب أن تسميها بشكل صريح (كما أخبر Poweroy) ، حذف الخط "DELETE 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;
  }

لديك شيء من هذا القبيل

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

إذا اتصلت بعد ذلك delete b;, ، لا يحدث شيء لـ a، ويكون لديك تسرب للذاكرة.تحاول أن تتذكر delete b->a; ليس حلا جيدا، ولكن هناك بضعة حلول أخرى.

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

هذا مدمر لـ B والذي سيحذف a.(إذا كانت القيمة 0، فلن يؤدي هذا الحذف إلى أي شيء.إذا لم تكن a 0 ولكنها لا تشير إلى الذاكرة من جديد، فستحصل على تلف الكومة.)

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

بهذه الطريقة لن يكون لديك مؤشر، بل auto_ptr<> (shared_ptr<> سيعمل أيضًا، أو مؤشرات ذكية أخرى)، ويتم حذفه تلقائيًا عندما يكون b.

أي من هاتين الطريقتين تعمل بشكل جيد، ولقد استخدمت كليهما.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top