Вызывает ли удаление указатель на подкласс деструктор базового класса?

165

У меня есть, class Aкоторый использует выделение кучи памяти для одного из своих полей. Класс A создается и сохраняется как поле указателя в другом классе ( class B.

Когда я закончу с объектом класса B, я вызываю delete, который, я полагаю, вызывает деструктор ... Но вызывает ли это и деструктор класса A?

Редактировать:

Из ответов, я беру это (пожалуйста, измените, если неправильно):

  1. delete экземпляра B вызывает B :: ~ B ();
  2. какие звонки A::~A();
  3. A::~A должен явно deleteвсе выделенные кучей переменные-члены объекта A;
  4. Наконец, блок памяти, хранящий указанный экземпляр класса B, возвращается в кучу - когда использовался new , он сначала выделял блок памяти в куче, затем вызывал конструкторы для его инициализации, теперь после того, как все деструкторы были вызваны для завершения объекта, блок, в котором находился объект, возвращается в кучу.
Ник Болтон
источник

Ответы:

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: Если вы вызываете delete по случайному указателю, значит, вы облажались. Там никогда не было веских причин для этого. На самом деле, если вы вручную вызываете delete в любом месте, кроме деструктора смарт-указателя, вам, вероятно, захочется еще раз взглянуть на то, почему вы не используете смарт-указатель или какой-либо другой диспетчер объектов.
Затмение
shared_array только от boost, да?
Дронц
30

Когда вы вызываете delete для указателя, выделенного новым, будет вызван деструктор указанного объекта.

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явно не сделаете это в Bдеструкторе. Если вы хотите автоматическое уничтожение, посмотрите на умные указатели, как auto_ptr.

Sharptooth
источник
4

Вы должны удалить A самостоятельно в деструкторе B.

Corné
источник
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;

Деструктор будет вызываться только в том случае, если в вашем базовом классе есть ключевое слово virtual.

Тогда, если у вас нет виртуального деструктора, вызывается только ~ B (). Но поскольку у вас есть виртуальный деструктор, сначала вызывается ~ D (), а затем ~ B ().

Никто из членов B или D, выделенных в куче, не будет освобожден, если вы явно не удалите их. И удаление их также вызовет их деструктор.

Брайан Р. Бонди
источник
1

У вас есть что-то вроде

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

Если вы затем позвоните delete b;, ничего не случится, и у вас утечка памяти. Попытка вспомнить 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

Указатель будет удален. Вы должны вызвать delete на A явным образом в деструкторе B.

RvdK
источник
Я делаю это, мой вопрос был вызван деструктор?
Ник Болтон
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;
  }
Дариус Кучинскас
источник