web-dev-qa-db-fra.com

Un destructeur est-il appelé lorsqu'un objet sort du cadre?

Par exemple:

int main() {
    Foo *leedle = new Foo();

    return 0;
}

class Foo {
private:
    somePointer* bar;

public:
    Foo();
    ~Foo();
};

Foo::~Foo() {
    delete bar;
}

Le destructeur serait-il implicitement appelé par le compilateur ou y aurait-il une fuite de mémoire?

Je suis nouveau dans la mémoire dynamique, donc si ce n'est pas un cas de test utilisable, je suis désolé.

35
Tux

Oui, les variables automatiques seront détruites à la fin du bloc de code englobant. Mais continuez à lire.

Le titre de votre question demande si un destructeur sera appelé lorsque la variable sortira du cadre. Vraisemblablement, ce que vous vouliez demander était:

le destructeur de Foo sera-t-il appelé à la fin de main ()?

Compte tenu du code que vous avez fourni, la réponse à cette question est non puisque l'objet Foo a une durée de stockage dynamique, comme nous le verrons bientôt.

Notez ici quelle est la variable automatique:

Foo* leedle = new Foo();

Ici, leedle est la variable automatique qui sera détruite. leedle n'est qu'un pointeur. La chose sur laquelle leedle pointe n'a pas une durée de stockage automatique et ne sera pas détruite. Donc, si vous faites cela:

void DoIt()
{
  Foo* leedle = new leedle;
}

Vous perdez la mémoire allouée par new leedle.


Vous devezdelete tout ce qui a été alloué avec new:

void DoIt()
{
  Foo* leedle = new leedle;
  delete leedle;
}

Ceci est rendu beaucoup plus simple et plus robuste en utilisant des pointeurs intelligents. En C++ 03:

void DoIt()
{
  std::auto_ptr <Foo> leedle (new Foo);
}

Ou en C++ 11:

void DoIt()
{
  std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}

Les pointeurs intelligents sont utilisés comme variables automatiques, comme ci-dessus, et lorsqu'ils sortent de la portée et sont détruits, ils automatiquement (dans le destructeur) delete l'objet pointé. Donc, dans les deux cas ci-dessus, il n'y a pas de fuite de mémoire.


Essayons d'éclaircir un peu le langage ici. En C++, les variables ont une durée de stockage. En C++ 03, il y a 3 durées de stockage:

1: automatique: Une variable avec une durée de stockage automatique sera détruite à la fin du bloc de code englobant.

Considérer:

void Foo()
{
  bool b = true;
  {
    int n = 42;
  } // LINE 1
  double d = 3.14;
} // LINE 2

Dans cet exemple, toutes les variables ont une durée de stockage automatique. b et d seront détruits à la LIGNE 2. n seront détruits à la LIGNE 1.

2: statique: Une variable avec une durée de stockage statique sera allouée avant le début du programme et détruite à la fin du programme.

3: dynamic: Une variable avec une durée de stockage dynamique sera allouée lorsque vous l'allouerez à l'aide des fonctions d'allocation de mémoire dynamique (par exemple, new) et sera détruite lorsque vous la détruisez à l'aide de la mémoire dynamique fonctions d'allocation (par exemple, delete).

Dans mon exemple original ci-dessus:

void DoIt()
{
  Foo* leedle = new leedle;
}

leedle est une variable avec une durée de stockage automatique et sera détruite à l'accolade de fin. La chose vers laquelle leedle pointe a une durée de stockage dynamique et n'est pas détruite dans le code ci-dessus. Vous devez appeler delete pour le désallouer.

C++ 11 ajoute également une quatrième durée de stockage:

4: thread: les variables avec la durée de stockage du thread sont allouées au début du thread et désallouées à la fin du thread.

89
John Dibling

Oui, si un objet sort de la portée, le destructeur est appelé. MAIS Non, le destructeur ne sera pas appelé dans ce cas, car vous n'avez qu'un pointeur dans la portée, ce pointeur n'a pas de destructeur particulier, donc il n'y aura pas d'appel indirect au destructeur de Foo.

Cet exemple est le domaine d'application des pointeurs intelligents comme std::unique_ptr et std::shared_ptr. Ce sont des classes réelles qui, contrairement aux pointeurs bruts ont un destructeur, appelant (conditionnellement) delete sur l'objet pointé.

Btw, le destructeur de Foo supprime bar, bur bar n'a jamais été initialisé ni assigné à une adresse qui pointe vers un objet réel, donc l'appel de suppression donnera un comportement indéfini, probablement un accident.

7
Arne Mertz

il y aurait effectivement une fuite de mémoire. Le destructeur de l'objet hors de portée (le Foo *) est appelé, mais pas celui de l'objet pointé (le Foo que vous avez alloué).

Techniquement parlant, puisque vous êtes dans le principal, ce n'est pas une fuite de mémoire, puisque jusqu'à ce que l'application ne soit pas terminée, vous pouvez accéder à chaque variable allouée. À cet égard, je cite Alexandrescu (de Modern C++, le chapitre sur les singletons)

Des fuites de mémoire apparaissent lorsque vous allouez des données accumulées et perdez toutes les références à celles-ci. Ce n'est pas le cas ici: rien ne s'accumule, et nous avons connaissance de la mémoire allouée jusqu'à la fin de l'application. En outre, tous les modernes

Bien sûr, cela n'implique pas que vous ne devez pas appeler delete, car ce serait une pratique extrêmement mauvaise (et dangereuse).

2
Stefano Falasca

Notez d'abord que le code ne compilerait pas; new renvoie un pointeur sur un objet alloué sur le tas. Vous avez besoin:

int main() {
    Foo *leedle = new Foo();
    return 0;
}

Maintenant, puisque new alloue l'objet avec un stockage dynamique plutôt qu'automatique, il ne sort pas du champ d'application à la fin de la fonction. Par conséquent, il ne sera pas supprimé non plus, et vous avez une fuite de mémoire.

1
Joni