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.
Nick Bolton
la source

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
Éclipse
la source
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

la source
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).

Sébastien Mach
la source
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.

dents acérées
la source
4

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

corné
la source
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.

Brian R. Bondy
la source
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.

David Thornley
la source
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".

Harri Luoma
la source
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.

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

Le destructeur de l'objet de classe A ne sera appelé que si delete est appelé pour cet objet. Assurez-vous de supprimer ce pointeur dans le destructeur de la classe B.

Pour un peu plus d'informations sur ce qui se passe lorsque la suppression est appelée sur un objet, voir: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9

Kris Kumler
la source
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;
  }
Darius Kucinskas
la source