web-dev-qa-db-fra.com

Le destructeur d'un membre de l'Union est-il appelé?

C++ 11 autorisait l'utilisation de types de disposition standard dans une union: le membre de l'Union avait un constructeur défini par l'utilisateur

Ma question est donc la suivante: suis-je assuré que le destructeur personnalisé sera appelé, lorsque la variable union sort de la portée?

D'après ce que j'ai compris, nous devons détruire et construire manuellement lors du basculement: http://fr.cppreference.com/w/cpp/language/union#Explanation

Mais qu'en est-il un exemple comme celui-ci:

{
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

Lorsque s est en dehors de la portée, ai-je perdu la mémoire de la chaîne allouée sur le tas parce que je n'ai pas appelé le destructeur de string?

18
Jonathan Mee

Dans votre exemple, vous avez fourni str ne sera pas détruit. Les états standard dans [class.union]/2

Une union peut avoir des fonctions membres (y compris des constructeurs et des destructeurs), mais pas des fonctions virtuelles (10.3). Une union ne doit pas avoir de classes de base. Une union ne doit pas être utilisée comme classe de base. Si une union contient un membre de données non statique de type référence, le programme est mal formé. Au plus un membre de données non statique d'une union peut avoir un initialiseur entre accolades ou égaux. [Remarque: Si un membre de données non statique d'une union possède un constructeur par défaut non trivial (12.1), un constructeur de copie (12.8), un constructeur de déplacement (12.8), un opérateur d'affectation de copie (12.8), un opérateur d'affectation de déplacement (12.8). ) ou destructeur (12.4), la fonction membre correspondante de l’union doit être fournie par l’utilisateur, sinon elle sera implicitement supprimée (8.4.3) pour l’union. - note de fin]

l'accent mien

Ainsi, étant donné que str et vec ont des fonctions spéciales pour les membres qui ne sont pas anodines, vous devrez les fournir vous-même pour le syndicat.

Notez que selon les commentaires de bogdan situés sous le destructeur vide, cela ne suffit pas. Dans [class.union]/8 nous avons

[...] Si X est une union, ses membres variantes sont les membres de données non statiques; [...]

Tous les membres de cette union sont donc des variantes. Ensuite, si nous regardons [class.dtor]/8 nous avons

Après avoir exécuté le corps du destructeur et détruit tous les objets automatiques alloués dans celui-ci, un destructeur de classe X appelle les destructeurs des membres de données directs non variables non variables de X [...]

Ainsi, le destructeur ne détruira pas automatiquement les membres de l'union, car ce sont des variantes.

Vous pouvez faire une union étiquetée comme _ { kennytm fait ici

struct TU {
   int type;
   union {
     int i;
     float f;
     std::string s;
   } u;

   TU(const TU& tu) : type(tu.type) {
     switch (tu.type) {
       case TU_STRING: new(&u.s)(tu.u.s); break;
       case TU_INT:    u.i = tu.u.i;      break;
       case TU_FLOAT:  u.f = tu.u.f;      break;
     }
   }
   ~TU() {
     if (tu.type == TU_STRING)
       u.s.~string();
   }
   ...
};

Ce qui garantit que le membre correct est détruit ou utilise simplement un std::variant ou boost::variant

15
NathanOliver

Votre exemple ne compilera pas. Les syndicats ont, par défaut, un destructeur supprimé. Bien sûr, quel destructeur devrait être appelé? Vous ne pouvez sûrement pas appeler les deux. Et nulle part aucune information n'est stockée sur le membre réellement construit. C'est à vous de fournir un destructeur approprié.

Voici le résultat de GCC lorsque vous essayez de compiler votre extrait de code:

In function ‘int main()’:
error: use of deleted function ‘main()::<anonymous union>::~<constructor>()’
       vector<int> vec; } s = { "Hello, world"s };
                                                ^

note: ‘main()::<anonymous union>::~<constructor>()’ is implicitly deleted because the default definition would be ill-formed:
      union { string str;
            ^
4
G. Sliepen

Vous devez toujours appeler manuellement le constructeur des objets de votre structure avec des types non triviaux.

Habituellement, vous devez aussi toujours les construire explicitement. Il semble étrange que la tâche ici fonctionne.

En cas de doute, vous pouvez toujours vérifier le montage si des destructeurs sont appelés.

L'assembly de ce code appelle le constructeur basic_string mais pas le destructeur. Donc, vous aurez des fuites ici.

using namespace std;
int main(int argc, char** argv){
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

lien pour voir l'Assemblée: https://godbolt.org/g/wKu3vf

1
Hayt