ลบตัวชี้ไปยังคลาสย่อยหรือไม่เรียกตัวทำลายคลาสพื้นฐานหรือไม่


165

ฉันมีอันclass Aที่ใช้การจัดสรรหน่วยความจำฮีปสำหรับหนึ่งในฟิลด์ของมัน คลาส A ถูกสร้างและเก็บเป็นฟิลด์ตัวชี้ในคลาสอื่น ( class B.

เมื่อฉันทำกับวัตถุของคลาส B ฉันเรียกdeleteซึ่งฉันคิดว่าเรียกว่า destructor ... แต่สิ่งนี้เรียกว่า destructor ของคลาส A ด้วยหรือไม่

แก้ไข:

จากคำตอบที่ฉันใช้ (โปรดแก้ไขหากไม่ถูกต้อง):

  1. delete ของอินสแตนซ์ของ B เรียก B :: ~ B ();
  2. สายไหน A::~A();
  3. A::~A ควรdeleteกำหนดตัวแปรสมาชิกที่จัดสรรฮีปทั้งหมดของวัตถุ A อย่างชัดเจน
  4. ในที่สุดการจัดเก็บหน่วยความจำบล็อกกล่าวว่าตัวอย่างของการเรียน B จะถูกส่งกลับไปยังกอง - เมื่อใหม่ถูกนำมาใช้เป็นครั้งแรกที่จัดสรรบล็อกของหน่วยความจำบนกองก่อสร้างแล้วเรียกเพื่อเริ่มต้นมันตอนนี้หลังจาก destructors ทั้งหมดได้รับการเรียกเพื่อจบวัตถุ บล็อกที่วัตถุที่อยู่จะถูกส่งกลับไปยังกอง

คำตอบ:


183

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และ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 ถูกเรียกเมื่อคุณเพิ่มหน่วยความจำเพียงบางส่วนเท่านั้น (เช่นใช้ตัวชี้ผิด)
Tomáš Zato - Reinstate Monica

ตัวชี้เป็นเพียงตัวเลข คุณสามารถใช้++โอเปอเรเตอร์กับมันได้โดยไม่ตั้งใจ ดังนั้นฉันสงสัยว่าตัวชี้ที่ชี้ตรงกลางของข้อมูลคลาสยังมีผลกระทบอยู่หรือไม่
Tomáš Zato - Reinstate Monica

2
@ TomášZato: ถ้าคุณโทรลบบนตัวชี้แบบสุ่มแสดงว่าคุณเมาแล้ว ไม่มีเหตุผลที่ดีที่จะทำเช่นนั้น ในความเป็นจริงถ้าคุณโทรหาการลบด้วยตนเองที่อื่นนอกเหนือจากตัวทำลายสมาร์ทพอยน์เตอร์คุณอาจต้องการดูครั้งที่สองว่าทำไมคุณไม่ใช้สมาร์ทพอยน์เตอร์หรือตัวจัดการวัตถุอื่น
Eclipse

shared_array มาจากการเพิ่มเท่านั้นใช่ไหม
Dronz

30

เมื่อคุณเรียกลบบนตัวชี้ที่จัดสรรโดยใหม่ตัวทำลายของวัตถุที่ชี้ไปจะถูกเรียก

A * p = new A;

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

22

มันมีชื่อว่า "destructor" ไม่ใช่ "deconstructor"

ภายใน destructor ของแต่ละคลาสคุณจะต้องลบตัวแปรสมาชิกอื่น ๆ ทั้งหมดที่ได้รับการจัดสรรใหม่

แก้ไข: เพื่อชี้แจง:

บอกว่าคุณมี

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 จัดสรรภายในจะถูกลบใน destructor ด้วย

แต่อินสแตนซ์ของคลาส C จะทำให้หน่วยความจำรั่วเพราะมันจะจัดสรรอินสแตนซ์ของ A ที่มันไม่ได้ปล่อยออกมา (ในกรณีนี้ C ไม่มีแม้แต่ destructor)


5

หากคุณมีตัวชี้ตามปกติ ( A*) ตัวทำลายจะไม่ถูกเรียก (และหน่วยความจำAเช่นจะไม่ถูกทำให้เป็นอิสระ) เว้นแต่คุณจะทำdeleteอย่างชัดเจนในตัวBทำลาย auto_ptrหากคุณต้องการดูการทำลายอัตโนมัติที่ชี้สมาร์ทเช่น



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;

destructor จะถูกเรียกก็ต่อเมื่อคลาสพื้นฐานของคุณมีคำหลักเสมือน

ถ้าคุณไม่มีตัวทำลายเสมือนเท่านั้น ~ B () จะถูกเรียก แต่เนื่องจากคุณมี destructor เสมือนก่อนอื่น ~ D () จะถูกเรียกแล้ว ~ B ()

จะไม่มีการจัดสรรสมาชิกของ B หรือ D บนฮีปยกเว้นว่าคุณลบอย่างชัดเจน และการลบพวกเขาจะเรียกผู้ทำลายล้างเช่นกัน


1

คุณมีสิ่งที่ชอบ

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 เป็น

วิธีเหล่านี้ใช้ได้ดีและฉันใช้ทั้งสองอย่าง


1

ฉันสงสัยว่าทำไมตัวทำลายในชั้นเรียนของฉันจึงไม่ถูกเรียก เหตุผลก็คือฉันลืมที่จะรวมคำจำกัดความของคลาสนั้น (#include "class.h") ฉันเพิ่งประกาศเช่น "class A;" และคอมไพเลอร์ก็มีความสุขกับมันและให้ฉันเรียกว่า "ลบ"


เพิ่มระดับการเตือนคอมไพเลอร์
Phil1970

0

ไม่ตัวชี้จะถูกลบ คุณควรเรียกการลบใน A อย่างชัดเจนใน destructor ของ B


ฉันกำลังทำสิ่งนี้คำถามของฉันคือ destructor ที่เรียกว่า?
Nick Bolton

0

destructor สำหรับวัตถุของคลาส A จะถูกเรียกใช้เมื่อการลบถูกเรียกสำหรับวัตถุนั้นเท่านั้น ตรวจสอบให้แน่ใจว่าได้ลบตัวชี้นั้นใน destructor ของคลาส B แล้ว

สำหรับข้อมูลเพิ่มเติมเล็กน้อยเกี่ยวกับสิ่งที่เกิดขึ้นเมื่อมีการเรียกใช้การลบบนวัตถุโปรดดูที่: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9


0

ไม่จะไม่เรียก 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;
  }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.