c++ - هل حذف استدعاء destructor؟




memory-management (10)

يجب عليك حذف A بنفسك في destructor من B.

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

عندما انتهيت من الكائن B ، أتصل بحذف ، والذي أفترض أنه يدعو destructor ... ولكن هل هذا استدعاء destructor في الفئة A كذلك؟

تصحيح:

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

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

سيتم تشغيل destructor من A عند انتهاء عمرها. إذا كنت تريد أن يتم تحرير الذاكرة الخاصة به وتشغيل destructor ، يجب عليك حذفها إذا تم تخصيصها على كومة الذاكرة المؤقتة. إذا تم تخصيصها على المكدس ، فسيحدث ذلك تلقائيًا (أي عندما يخرج من النطاق ؛ راجع 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 و 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

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


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;

سيتم استدعاء destructor فقط إذا كان الصف الأساسي الخاص بك يحتوي على الكلمة الأساسية الظاهرية.

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

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


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

A * p = new A;

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

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


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

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


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


لا لن يطلق عليه destructor للفئة A ، يجب عليك الاتصال به بشكل صريح (مثل PoweRoy قال) ، حذف السطر "حذف 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;
  }

أولاً ، لاحظ أنه نظرًا لأن Java عبارة عن تجميع قمامة ، فمن النادر أن تحتاج إلى عمل أي شيء حيال تدمير الكائنات. أولاً لأنك لا تملك عادةً أي موارد مدارة لتحريرها ، وثانيًا لأنك لا تستطيع التنبؤ بموعد حدوثه أو حدوثه ، لذلك لا يكون مناسبًا للأشياء التي تحتاج إلى حدوثها "بمجرد أن لا يستخدم أحد شيئًا ".

يمكن أن يتم إعلامك بعد أن يتم تدمير كائن باستخدام java.lang.ref.PhantomReference (في الواقع ، قد يكون قد تم إتلافه غير دقيق بعض الشيء ، ولكن إذا تم وضع إشارة فانتوم عليه في قائمة الانتظار ، فهذا يعني أنه لم يعد من الممكن استرداده ، والذي عادة ما يصل إلى نفس الشيء). الاستخدام الشائع هو:

  • قم بفصل المورد (الموارد) في الفصل الدراسي الذي يجب أن يتم تدميره في كائن مساعد آخر (لاحظ أنه إذا كان كل ما تفعله هو إغلاق الاتصال ، وهي حالة شائعة ، فلن تحتاج إلى كتابة فصل جديد: سيكون الاتصال ليتم إغلاق "كائن المساعد" في هذه الحالة).
  • عند إنشاء الكائن الرئيسي ، أنشئ أيضًا PhantomReference إليه. إما أن يكون هذا يشير إلى كائن مساعد جديد ، أو إعداد خريطة من كائنات PhantomReference لكائنات مساعدهم المقابلة.
  • بعد أن يتم جمع الكائن الرئيسي ، يتم وضع قائمة الانتظار في PhantomReference (أو بالأحرى قد يكون في قائمة الانتظار - مثل finalizers لا يوجد أي ضمان على الإطلاق ، على سبيل المثال إذا خرج VM فإنه لن ينتظر). تأكد من معالجة قائمة الانتظار الخاصة به (سواء في مؤشر ترابط خاص أو من وقت لآخر). نظرًا للإشارة الثابتة إلى كائن مساعد ، لم يتم بعد تجميع كائن المساعد. لذا افعل ما تريده من تنظيف على الكائن المساعد ، ثم تجاهل PhantomReference وسيتم جمع المساعد في النهاية أيضًا.

هناك أيضا وضع اللمسات الأخيرة () ، والتي تبدو وكأنها المدمرة ولكن لا تتصرف مثل واحد. انها عادة ليست خيارا جيدا.







c++ memory-management destructor