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

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

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

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

Насколько я понимаю, из ответов (пожалуйста, отредактируйте, если он неверен):

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

person Nick Bolton    schedule 24.03.2009    source источник


Ответы (11)


Деструктор 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
person Eclipse    schedule 24.03.2009
comment
Интересно, вызывается ли деструктор, когда вы освобождаете память только частично (например, используя неправильный указатель) - person Tomáš Zato - Reinstate Monica; 06.12.2014
comment
Указатель - это просто число. Вы даже можете случайно использовать на нем оператор ++. Поэтому мне интересно, действует ли указатель, указывающий на середину данных класса. - person Tomáš Zato - Reinstate Monica; 19.12.2014
comment
@ TomášZato: Если вы вызываете delete для случайного указателя, то вы облажались. Для этого никогда не было веской причины. Фактически, если вы вручную вызываете удаление в любом месте, кроме деструктора интеллектуального указателя, вы, вероятно, захотите еще раз взглянуть, почему вы не используете интеллектуальный указатель или какой-либо другой диспетчер объектов. - person Eclipse; 19.12.2014
comment
shared_array только от boost, да? - person Dronz; 22.05.2018

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

A * p = new A;

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

Он называется «деструктор», а не «деконструктор».

Внутри деструктора каждого класса вы должны удалить все другие переменные-члены, которые были выделены с помощью 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 даже не имеет деструктора).

person Sebastian Mach    schedule 24.03.2009

Если у вас есть обычный указатель (A*), то деструктор не будет вызываться (и память для экземпляра A также не будет освобождена), если вы не сделаете delete явно в деструкторе B. Если вы хотите автоматическое уничтожение, посмотрите на умные указатели, такие как auto_ptr.

person sharptooth    schedule 24.03.2009

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

person corné    schedule 24.03.2009

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, выделенных в куче, не будет освобожден, если вы явно не удалите их. И удаление их также вызовет их деструктор.

person Brian R. Bondy    schedule 24.03.2009

У тебя есть что-то вроде

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, но не указывает на память из new, вы получите повреждение кучи.)

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

Таким образом, у вас не будет a в качестве указателя, а скорее будет auto_ptr ‹> (также подойдет shared_ptr‹> или другие интеллектуальные указатели), и он автоматически удаляется, когда есть b.

Любой из этих способов работает хорошо, и я использовал оба.

person David Thornley    schedule 24.03.2009

Мне было интересно, почему не был вызван деструктор моего класса. Причина в том, что я забыл включить определение этого класса (#include "class.h"). У меня было только объявление типа "class A;" и компилятор был доволен этим и позволил мне вызвать «удалить».

person Harri Luoma    schedule 17.08.2017
comment
Увеличить уровень предупреждений компилятора - person Phil1970; 17.07.2018

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

person RvdK    schedule 24.03.2009
comment
Я делаю это, мой вопрос был вызван ли деструктор? - person Nick Bolton; 24.03.2009

Деструктор для объекта класса A будет вызываться только в том случае, если для этого объекта вызывается удаление. Обязательно удалите этот указатель в деструкторе класса B.

Для получения дополнительной информации о том, что происходит при вызове удаления для объекта, см. http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9

person Kris Kumler    schedule 24.03.2009

нет, он не будет вызывать деструктор для класса 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;
  }
person Darius Kucinskas    schedule 24.03.2009