La suppression sur un pointeur vers une sous-classe appelle-t-elle le destructeur de classe de base?


165

J'ai un class Aqui utilise une allocation de mémoire de tas pour l'un de ses champs. La classe A est instanciée et stockée en tant que champ de pointeur dans une autre classe ( class B.

Quand j'en ai fini avec un objet de classe B, j'appelle delete, ce que je suppose appelle le destructeur ... Mais cela appelle-t-il aussi le destructeur de classe A?

Éditer:

D'après les réponses, je prends cela (veuillez modifier si incorrect):

  1. delete d'une instance de B appelle B :: ~ B ();
  2. qui appelle A::~A();
  3. A::~A devrait explicitement deletetoutes les variables membres allouées au tas de l'objet A;
  4. Enfin, le bloc de mémoire stockant ladite instance de la classe B est retourné au tas - quand new a été utilisé, il a d'abord alloué un bloc de mémoire sur le tas, puis a appelé des constructeurs pour l'initialiser, maintenant après que tous les destructeurs ont été appelés pour finaliser l'objet, le bloc où résidait l'objet est renvoyé dans le tas.

Réponses:


183

Le destructeur de A fonctionnera lorsque sa durée de vie sera terminée. Si vous voulez que sa mémoire soit libérée et que le destructeur s'exécute, vous devez le supprimer s'il a été alloué sur le tas. S'il a été alloué sur la pile, cela se produit automatiquement (c'est-à-dire quand il est hors de portée; voir RAII). S'il s'agit d'un membre d'une classe (pas un pointeur, mais un membre à part entière), cela se produira lorsque l'objet conteneur est détruit.

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

Dans l'exemple ci-dessus, chaque suppression et suppression [] est nécessaire. Et aucune suppression n'est nécessaire (ni même utilisable) là où je ne l'ai pas utilisée.

auto_ptr, unique_ptret shared_ptretc ... sont parfaits pour rendre cette gestion de la vie beaucoup plus facile:

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

Je me demande si le destructeur est appelé lorsque vous ne libérez que partiellement la mémoire (par exemple en utilisant un mauvais pointeur)
Tomáš Zato - Réintégrer Monica

Le pointeur n'est qu'un nombre. Vous pouvez même utiliser accidentellement un ++opérateur dessus. Je me demande donc si le pointeur qui pointe au milieu des données de classe a toujours l'effet.
Tomáš Zato - Réintégrer Monica

2
@ TomášZato: Si vous appelez delete sur un pointeur aléatoire, vous êtes foutu. Il n'y a jamais de bonne raison de faire ça. En fait, si vous appelez manuellement delete n'importe où autre qu'un destructeur de pointeur intelligent, vous voudrez probablement examiner à nouveau pourquoi vous n'utilisez pas de pointeur intelligent ou un autre gestionnaire d'objets.
Eclipse

shared_array provient uniquement de boost, oui?
Dronz

30

Lorsque vous appelez delete sur un pointeur alloué par new, le destructeur de l'objet pointé sera appelé.

A * p = new A;

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

22

Il est nommé "destructeur", pas "déconstructeur".

Dans le destructeur de chaque classe, vous devez supprimer toutes les autres variables membres qui ont été allouées avec new.

edit: Pour clarifier:

Dis que tu as

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;
}

L'allocation d'une instance de B, puis la suppression est propre, car ce que B alloue en interne sera également supprimé dans le destructeur.

Mais les instances de la classe C perdront de la mémoire, car elle alloue une instance de A qu'elle ne libère pas (dans ce cas, C n'a même pas de destructeur).


5

Si vous avez un pointeur habituel ( A*) alors le destructeur ne sera pas appelé (et la mémoire par Aexemple ne sera pas libérée non plus) à moins que vous ne deletele fassiez explicitement dans Ble destructeur de. Si vous voulez une destruction automatique, regardez des pointeurs intelligents comme auto_ptr.


4

Vous devez supprimer A vous-même dans le destructeur de B.


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;
    }
};

Quand vous faites:

B *pD = new D();
delete pD;

Le destructeur ne sera appelé que si votre classe de base a le mot-clé virtual.

Ensuite, si vous n'aviez pas de destructeur virtuel, seul ~ B () serait appelé. Mais puisque vous avez un destructeur virtuel, d'abord ~ ​​D () sera appelé, puis ~ B ().

Aucun membre de B ou D alloué sur le tas ne sera désalloué à moins que vous ne les supprimiez explicitement. Et les supprimer appellera également leur destructeur.


1

Vous avez quelque chose comme

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

Si vous appelez ensuite delete b;, rien ne se passe à a et vous avez une fuite de mémoire. Essayer de s'en souvenir delete b->a;n'est pas une bonne solution, mais il y en a quelques autres.

B::~B() {delete a;}

Il s'agit d'un destructeur pour B qui supprimera un fichier. (Si a vaut 0, cette suppression ne fait rien. Si a n'est pas 0 mais ne pointe pas vers la mémoire depuis new, vous obtenez une corruption du tas.)

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

De cette façon, vous n'avez pas a comme pointeur, mais plutôt un auto_ptr <> (shared_ptr <> fera également l'affaire, ou d'autres pointeurs intelligents), et il est automatiquement supprimé lorsque b l'est.

L'une ou l'autre de ces méthodes fonctionne bien, et j'ai utilisé les deux.


1

Je me demandais pourquoi le destructeur de ma classe n'était pas appelé. La raison en était que j'avais oublié d'inclure la définition de cette classe (#include "class.h"). Je n'avais qu'une déclaration comme "classe A"; et le compilateur en était satisfait et permettez-moi d'appeler "supprimer".


Augmenter le niveau d'avertissement du compilateur
Phil1970

0

Non, le pointeur sera supprimé. Vous devez appeler la suppression sur A explicite dans le destructeur de B.


Je fais cela, ma question était est-ce que le destructeur est appelé?
Nick Bolton


0

non, il n'appellera pas le destructeur pour la classe A, vous devriez l'appeler explicitement (comme PoweRoy l'a dit), supprimez la ligne 'delete ptr;' en exemple pour comparer ...

  #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;
  }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.