Чи видаляє вказівник на підклас виклик деструктора базового класу?


165

У мене є an, class Aякий використовує розподіл пам'яті купи для одного з його полів. Клас А інстанціюється і зберігається як поле вказівника в іншому класі ( class B.

Коли я закінчую з об'єктом класу B, я дзвоню delete, який, як я вважаю, називає деструктором ... Але це також називає деструктор класу A?

Редагувати:

З відповідей я беру це (будь ласка, відредагуйте, якщо невірно):

  1. delete екземпляра B викликів B :: ~ B ();
  2. який дзвонить A::~A();
  3. A::~A повинні явно deleteвсі змінні член-об'єкти об'єкта A, виділені з купи;
  4. Нарешті, блок пам'яті, що зберігає згаданий екземпляр класу B, повертається до купи - коли було використано нове , він спочатку виділив блок пам'яті на купу, потім викликав конструкторів, щоб ініціалізувати його, тепер після того, як всі деструктори будуть викликані для доопрацювання об'єкта об'єктом блок, де об'єкт перебував, повертається до купи.

Відповіді:


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: Якщо ви зателефонуєте на видалення за випадковим вказівником, то вас накрутили. Ніколи не є вагомих причин для цього. Насправді, якщо ви вручну викликаєте видалення в будь-якому місці, окрім деструктора смарт-покажчика, ви, ймовірно, хочете по-другому подивитися, чому ви не використовуєте смарт-покажчик чи інший менеджер об'єктів.
Затемнення

shared_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 виділяє внутрішньо, також буде видалено в деструкторі.

Але екземпляри класу C просочать пам'ять, оскільки він виділяє екземпляр A, який він не випускає (у цьому випадку C навіть не має деструктора).


5

Якщо у вас є звичайний покажчик ( A*), то деструктор не буде викликаний (і пам'ять, Aнаприклад, не буде звільнена), якщо ви deleteявно не зробите це в програмі Bs. Якщо ви хочете автоматичного знищення, подивіться на розумні покажчики 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;

Викликати деструктор буде лише у тому випадку, якщо ваш базовий клас має віртуальне ключове слово.

Тоді, якщо у вас не було віртуального деструктора, буде викликано тільки ~ B (). Але оскільки у вас є віртуальний деструктор, спочатку буде викликано ~ 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. (Якщо a дорівнює 0, видалення нічого не робить. Якщо a не 0, але не вказує на пам'ять з нового, ви отримуєте пошкодження.)

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

Таким чином, у вас немає як вказівник, а швидше auto_ptr <> (так само буде зроблено shared_ptr <> або інші смарт-покажчики), і він автоматично видаляється, коли b є.

Будь-який із цих способів працює добре, і я використовував обидва.


1

Мені було цікаво, чому не викликали деструктора мого класу. Причиною було те, що я забув включити визначення цього класу (#include "class.h"). У мене була лише декларація на кшталт "клас А;" і компілятор був задоволений цим і дозволив мені зателефонувати "видалити".


Підвищити рівень попередження компілятора
Phil1970

0

Ні. Вказівник буде видалений. Вам слід зателефонувати на видалення на A явне в деструкторі B.


Я роблю це, моє запитання: чи називається деструктор?
Нік Болтон

0

Деструктор для об’єкта класу A буде викликатися лише тоді, коли для цього об’єкта буде викликано delete. Обов’язково видаліть цей покажчик у деструкторі класу B.

Для отримання додаткової інформації про те, що відбувається, коли видалення викликається на об’єкті, дивіться: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9


0

ні, він не зателефонує деструктору для класу 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;
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.