Ordre de destruction C ++: appel d'un destructeur de champ avant le destructeur de classe


CK.

Existe-t-il un moyen d'appeler un destructeur de champ avant le destructeur de classe?

Supposons que j'ai 2 classes Smallet Big, et Bigcontienne une instance de Smallcomme son champ en tant que tel:

class Small
{
public:
    ~Small() {std::cout << "Small destructor" << std::endl;}
};

class Big
{
public:
    ~Big() {std::cout << "Big destructor" << std::endl;}

private:
    Small small;
};

int main()
{
    Big big;
}

Ceci, bien sûr, appelle le grand destructeur avant le petit destructeur:

Big destructor
Small destructor

J'ai besoin que le Smalldestructeur soit appelé avant le Bigdestructeur car il effectue le nettoyage nécessaire pour le Bigdestructeur.

Je pouvais:

  1. appelez small.~Small()explicitement le destructeur. -> Ceci, cependant, appelle le Smalldestructeur deux fois: une fois explicitement, et une fois après l' Bigexécution du destructeur.
  2. avoir un Small*comme champ et appeler delete small;le Bigdestructeur

Je suis conscient que je peux avoir une fonction dans la Smallclasse qui fait le nettoyage et l'appeler dans le Bigdestructeur, mais je me demandais s'il y avait un moyen d'inverser l'ordre du destructeur.

Y a-t-il une meilleure façon de faire cela?

skypjack

appelez explicitement le destructeur small. ~ Small (). -> Ceci, cependant, appelle le petit destructeur deux fois: une fois explicitement, et une fois après l'exécution du gros destructeur.

Eh bien, je ne sais pas pourquoi vous voulez continuer avec cette conception défectueuse, mais vous pouvez résoudre le problème décrit dans votre première puce en utilisant le placement nouveau.
Il suit un exemple de travail minimal:

#include <iostream>

struct Small {
    ~Small() {std::cout << "Small destructor" << std::endl;}
};

struct Big {
    Big() { ::new (storage) Small; }

    ~Big() {
        reinterpret_cast<Small *>(storage)->~Small();
        std::cout << "Big destructor" << std::endl;
    }

    Small & small() {
        return *reinterpret_cast<Small *>(storage);
    }

private:
    unsigned char storage[sizeof(Small)];
};

int main() {
    Big big;
}

Vous n'avez plus de variable de type Small, mais avec quelque chose comme la smallfonction membre dans l'exemple, vous pouvez facilement la contourner.

L'idée est que vous réservez suffisamment d'espace pour construire un sur place Small, puis vous pouvez invoquer son destructeur explicitement comme vous l'avez fait. Il ne sera pas appelé deux fois, car tout ce que la Bigclasse doit libérer est un tableau de unsigned chars.
De plus, vous ne le stockerez pas Smalldirectement dans le stockage dynamique, car en fait, vous utilisez une donnée membre de votre Bigpour le créer.


Cela étant dit, je vous suggère de l'allouer sur le stockage dynamique à moins que vous n'ayez une bonne raison de faire autrement. Utilisez a std::unique_ptret réinitialisez-le au début du destructeur de Big. Votre Smallva disparaître avant que le corps du destructeur ne soit réellement exécuté comme prévu et dans ce cas également, le destructeur ne sera pas appelé deux fois.


ÉDITER

Comme suggéré dans les commentaires, std::optionalpeut être une autre solution viable au lieu de std::unique_ptr. Gardez à l'esprit que cela std::optionalfait partie du C ++ 17, donc si vous pouvez l'utiliser, cela dépend principalement de la révision de la norme à laquelle vous devez adhérer.

Articles connexes


Renvoyer le destructeur de classe

Ciprian Ambrus Je suis étudiant et j'apprends le C ++. Je suis assez bon en C ++, encore des choses «simples» m'empêtrent. J'ai récemment appris des classes, des méthodes, des constructeurs / déconstructeurs, des héritages, des virtuels, etc. J'ai ce code: #in

Comportement étrange d'un destructeur de classe en C ++

Xeenych Xeenych #include <iostream> using namespace std; class CCC { public: CCC() {cout<<"created "<<++count<<endl;} ~CCC(){cout<<"deleted "<<--count<<endl;} int count=0; }; CCC a; CCC& create() { return a; } int main () { CCC result

Supprimer le destructeur de lancement appelé depuis le destructeur

Mircea Baja Je comprends que les règles actuelles en C ++ disent que: si un destructeur lance alors que la pile se déroule déjà à cause d'une exception, alors std::terminateest appelé. En explorant pourquoi les règles sont telles qu'elles sont, je suis tombé s

Le destructeur de la classe enfant n'est pas appelé

Alexandre Je travaille actuellement sur un jeu en tant que projet pour mon université. Il est réalisé en C++ avec SDL2. J'ai un vecteur qui contient des pointeurs de la classe Enemies, qui est une classe parente abstraite de la Plantclasse. Dans le constructeu

Appel de destructeur

Épouvantail Je suis nouveau en C++ et je suis en train de l'apprendre. Pendant que j'explorais le Web et les problèmes, je suis tombé sur le code suivant : class A { public: ~A() { cout << "A"; } }; class B { public: ~B() { cout << "B"; }