Вызывает ли удаление указатель на подкласс деструктор базового класса? - PullRequest
159 голосов
/ 24 марта 2009

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

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

Edit:

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

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

Ответы [ 11 ]

179 голосов
/ 24 марта 2009

Деструктор 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
29 голосов
/ 24 марта 2009

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

A * p = new A;

delete p;    // A:~A() called for you on obkect pointed to by p
22 голосов
/ 24 марта 2009

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

Внутри деструктора каждого класса вы должны удалить все остальные переменные-члены, которые были назначены с новым.

изменить: уточнить:

Скажем, у вас есть

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 голосов
/ 24 марта 2009

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

4 голосов
/ 24 марта 2009

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

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

1 голос
/ 17 августа 2017

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

0 голосов
/ 24 марта 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. (Если a равно 0, это удаление ничего не делает. Если a не равно 0, но не указывает на память из нового, вы получаете повреждение кучи.)

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

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

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

0 голосов
/ 24 марта 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;
  }
0 голосов
/ 24 марта 2009

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

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

...