Supposons que j'ai une classe dont le constructeur apparaît un fil qui supprime l'objet:
class foo { public: foo() : // initialize other data-members , t(std::bind(&foo::self_destruct, this)) {} private: // other data-members std::thread t; // no more data-members declared after this void self_destruct() { // do some work, possibly involving other data-members delete this; } };
4 Réponses :
Le comportement que vous comptez sur est qu'il est souvent possible d'appeler une fonction de membre non statistique non virtuelle sur un objet morte, à condition que cette fonction membre n'accède pas réellement SECTION 3.8P6 de la norme Le rend un comportement non défini si un objet n'est pas en direct lors d'un appel à une fonction de membre non statique: P>
De même, avant que la durée de vie d'un objet ait commencé mais après le stockage que l'objet occupera a été alloué ou, après la fin de la durée de vie d'un objet et avant la fin du stockage que l'objet occupé est réutilisé ou libéré, tout glvalue Cela fait référence à l'objet d'origine peut être utilisé mais uniquement de manière limitée. Pour un objet en construction ou destruction, voir 12,7. Sinon, une telle glvalue fait référence à alloué
Le stockage et l'utilisation des propriétés de la glvalue qui ne dépendent pas de sa valeur est bien définie. Le programme a un comportement non défini si: p>
Pour ce cas spécifique (supprimant un objet en construction), nous trouvons à la section 5.3.5P2: P>
... dans la première alternative ( Suppr Object em>), la valeur de l'opérande de Cette exigence n'est pas remplie. supprimer ceci; code> fonctionne correctement dans la pratique sur la plupart des plates-formes; Certains peuvent même garantir un comportement correct en tant qu'extension spécifique à la plate-forme. Mais IIRC il n'est pas bien défini en fonction de la norme. P>
ceci code> . Mais ce comportement n'est pas autorisé par la norme; Il est au mieux non-portable. P>
static_cast code> sauf lorsque la conversion est finalement
cv code> em>
char et code> ou
CV code> em>
non signé Char & Code>, ou li>
dynamic_cast code> ou comme opérande de
typeid code>. Li>
ul>
blockQuote>
Supprimer code> peut être une valeur de pointeur null, un pointeur sur un non-tableau Objet créé par un précédent nouvel expression em> ou un pointeur à une sous-observation représentant une classe de base d'un tel objet (clause 10). Sinon, le comportement n'est pas défini. Dans la deuxième alternative ( Suppry Array em>), la valeur de l'opérande de
Supprimer code> peut être une valeur de pointeur NULL ou une valeur de pointeur résultant d'un tableau précédent neuf -expression em>. Sinon, le comportement est indéfini. P>
blockQuote>
* Ce code> n'est pas un objet créé em>, passé, par un nouvelle expression em>. C'est un objet étant créé em> (présent progressif). Et cette interprétation est prise en charge par l'affaire Array, où le pointeur doit être le résultat d'un précédent nouvelle expression em> ... mais le nouvelle expression em> n'est pas encore complètement évalué ; Ce n'est pas Précédent em> et il n'a pas encore de résultat. P>
"Mais IIRC [Supprimer cela] n'est pas bien défini en fonction de la norme." Il est.
@ALF: Référence S'il vous plaît, j'aimerais examiner cette règle.
Je pense que vous pourriez mettre fin à la citation juste après le "pour un objet en construction ou la destruction, voir 12,7".
Il n'y a pas de règle spéciale pour permettre l'informatique 2 + 2 code>, et il n'y a pas de règle spéciale pour permettre
supprimer ce code>. Mais il y a des règles contre l'accès des trucs dans un objet détruit, c'est ce que l'on doit éviter après un
supprimer ce code>. La plupart des cadres d'interface graphique font
supprimer ce code>, donc c'est une fonctionnalité assez cruciale.
@Alf: Et la règle que j'ai citée peut être interprétée pour interdire un appel à une fonction de membre non statique avec la destruction de l'objet. (Une fonction appelle-t-elle les transferts de contrôle instantanés ou la durée de l'exécution de la fonction?)
@Benvoigt Peut-être que je suis mal compris ce texte, mais il semble que cela ne parle que de glissades qui font référence à des objets qui ne sont pas en construction ni de destruction, mais l'objet en question est en construction.
@sftrabbit: J'essaie de répondre à un sujet plus large de "Puis-je permettre à un objet d'être supprimé pendant que ses membres exécutaient?" Une fois que le Delier est invoqué, l'objet n'est pas en construction, c'est mort, et cette règle s'appliquerait aussi.
@Bensvoight mais à ce point, le Glvalue a déjà été utilisé pour appeler la fonction de membre non statique, n'est-ce pas? Oui, l'exécution se produit après la destruction, mais est-ce un problème? Il ne dit que ici que appeler i> la fonction entraînerait un comportement indéfini.
@sftrabbit: Comme je l'ai dit dans mon commentaire précédent, quelle est la durée d'un appel? L'instruction d'appel, ou l'ensemble de l'exécution jusqu'à la (éventuellement implicite) retour code> est atteint?
Tout d'abord, nous voyons qu'un objet de type On dit qu'un objet a une initialisation non triviale s'il s'agit d'un type de classe ou d'agrégation et que celui-ci ou un de ses membres est initialisé par un constructeur autre qu'un constructeur par défaut trivial. P>
blockQuote>
Nous voyons maintenant qu'un objet de type La durée de vie d'un objet de type Maintenant, c'est un comportement non défini si vous faites Avant que la durée de vie d'un objet ait commencé mais après le stockage que l'objet occupera a été alloué à tout pointeur [...] qui fait référence à l'emplacement de stockage où l'objet sera ou qu'il était situé peut être utilisé mais seulement dans façons limitées. Pour un objet en construction ou destruction, voir 12,7. Sinon, [...] p>
blockQuote>
Donc, depuis notre objet sous la construction, nous examinons le §12.7: P>
Les fonctions membres, y compris les fonctions virtuelles (10.3), peuvent être appelées pendant la construction ou la destruction (12.6.2). P>
blockQuote>
Cela signifie que c'est bien pour Premier, il "invoquera le destructeur (le cas échéant) pour l'objet [...] étant supprimé". Le destructeur est un cas particulier de la fonction membre, il convient donc de l'appeler. Cependant, §12.4 Destructeurs ne dit rien de savoir s'il est bien défini em> lorsque le destructeur est appelé pendant la construction. Pas de chance ici. P>
Deuxièmement, "L'expression Supprimer-expression em> appellera une fonction ometlocation em>" et "la fonction DealLocation doit annoncer le stockage référencé par le pointeur". Encore une fois, rien n'est dit de faire cela au stockage qui est actuellement utilisé soit un objet en construction. P>
Je soutiens donc que cela est un comportement indéfini par le fait que la norme ne l'a pas défini très précisément. P>
Juste pour noter: la durée de vie d'un objet de type foo code> a une initialisation non triviale car son constructeur est non trivial (§3.8 / 1): p>
FOO code> S commence après la fin du constructeur (§3.8 / 1): p>
t code> commence lorsque: p>
Supprimer code> sur l'objet avant la fin du constructeur si em> le type
foo code> a un non -Trivial destructeur (§3.8 / 5): P>
Self_Destract code> à appeler tandis que l'objet est construit. Cependant, cette section ne dit rien spécifiquement de la destruction d'un objet alors qu'elle est construite. Donc, je suggère que nous examinons l'opération du
Supprimer-expression code>. P>
FOO code> extrémise em> lorsque l'appel des destructeurs commence, car il a un destructeur non trivial. Donc, si
supprime ceci; code> se produit avant la fin de la construction de l'objet,
Cela interdit de manière convaincante le cas en discussion, mais je pense que cela peut être invalide même avec un destructeur trivial, car la norme ne définit pas le comportement à moins qu'un objet ne vit pendant la durée d'un appel à une fonction de membre non statique.
@Benvoigt: Je ne dirais pas "de manière convaincante" ... ;-)
@ Cheersandhth.-Alf Comment puis-je vous convaincre? :(
UHM, je devais supprimer ma réponse. pas certain. Mais je pense à cet objet comme un sous-objet d'un autre. Ensuite, c'est sûrement UB de toute façon. Je ne pense pas que cette réponse abordée par cette réponse.
@sftrabbit: Ok j'ai trouvé où il se passe mal: la citation du §3.8 / 5 laisse la partie - indiquée par Ellipsis - où il est indiqué que le point de balle ne s'applique pas B>. Au lieu de cela pour le cas, il dirige le §12.7; Citation "Pour un objet en construction ou de destruction, voir 12.7". Donc, cette réponse n'est certainement pas :-(, mais ma propre réponse n'est pas assez bonne non plus, désolée.
-1 Ouch Réponse incorrecte (§3.8 / 5 Bullet Point ne s'applique pas) Sélectionné en tant que solution, je dois indiquer que, malheureusement, le seul moyen visible d'indiquer une incorrecture.
@ Cheersandhth.-Alf J'ai fait de mon mieux, mais je pense que cela est mal défini. J'aimerais pouvoir supprimer mon accept.
0 Support de vovot après la correction. Cela peut être le meilleur qui puisse être dit.
officiellement, l'objet n'existe pas tant que le constructeur ait terminé avec succès. Une partie de la raison est que le constructeur peut être appelé à partir d'un constructeur de classe dérivé. Dans ce cas, vous ne voulez certainement pas détruire le sous-objet construit via un appel de destructeur explicite, et même moins invoquer ub en appelant StandardSee sur l'existence de l'objet, l'accent ajouté: p>
Le constructeur dans ce cas est non trivial juste en étant fourni par l'utilisateur. P> supprimer ce code> sur une (partie d'a) pas entièrement construit objet. p>
La Lifetime d'un objet est une propriété d'exécution de l'objet. Un objet est dit avoir une initialisation non triviale
S'il est d'une classe ou d'un type d'agrégat et que celui-ci ou un de ses membres est initialisé par un constructeur autre qu'un trivial
constructeur par défaut. [ Remarque: em> l'initialisation par une copie triviale / constructeur de déplacement est une initialisation non triviale. -end Note em>] La durée de vie d'un objet de type T commence lorsque:
- Stockage avec l'alignement et la taille appropriés pour le type T est obtenu et
- Si l'objet a une initialisation non triviale, son initialisation est terminé em> fort>. P>
blockQuote>
Citations s'il vous plaît. Bravo et hth.
@LightnessRacesinorbit: J'ai ajouté les normes pertinentes que j'ai trouvées, mais je pense que c'est un cas de quelque chose d'UB à titre de ne pas être explicitement autorisé (à savoir non explicitement autorisé par §12.7). Ces cas exigent généralement beaucoup de travail à clouer. Je suis désolé, mais ma réponse ici est plus sur le niveau de renonciation à la main et je n'ai pas le temps de faire la recherche prolongée :-(
Je suis à peu près sûr que cette citation couvre.
@Benvoigt: Non, je n'ai pas dit ce que vous avez trompeusement paraphrase. Mais de toute façon, dans le cas de ce que vous pouvez faire avec un pointeur à un objet en construction, il existe un libellé vague au §3.8 / 5 pour "voir §12.7". Et dans cette section, certaines choses qui peuvent être faites (par exemple typeid code> et
dynamic_cast code>) sont explicitement répertoriés. Cela dit (qui devrait aborder votre perplexe sur la raison pour laquelle les situations sont différentes), comme je l'ai noté à Tomalak, je ne pense pas que cette réponse soit très solide, du tout. Il se peut que la norme ne soit pas complètement claire ici, auquel cas nous sommes d'interprétation ...
i daesay, il est bien défini comme illégal (bien que cela puisse toujours travailler toujours avec certains compilateurs). P>
Ceci est quelque peu la même situation que "destructeurs non appelés quand une exception est jetée du constructeur". P>
Une expression de suppression, selon la norme, détruit un objet le plus dérivé (1.8) ou une matrice créée par une nouvelle expression em> (5.3.2). Avant la fin du constructeur, un objet n'est pas em> un objet le plus dérivé, mais un objet de son type d'ancêtre direct. p>
Votre classe FOO code> n'a pas de classe de base, il n'y a donc pas d'ancêtres,
ceci code> n'a donc pas de type et votre objet n'est pas vraiment un objet du tout à l'époque < Code> Supprimer code> est appelé. Mais même s'il y avait une classe de base, l'objet serait un objet non dérivé (le rendu toujours illégal) et le mauvais constructeur serait appelé. P>
Vous pouvez finir par appeler le destructeur
T code> avant que son constructeur soit pleinement exécuté. Ce serait sûrement ub.
En ce qui concerne la résolution du problème, que diriez-vous uniquement de la construction de
FOO code> à partir d'une fonction d'usine, qui crée un
partagé_ptr code> vous pouvez remettre le fil? (en utilisant Shared_From_This aurait été préférable, mais que n'est pas une option )
Dans cet exemple, qu'est-ce qui est plus inquiétant, certains sont que l'objet de thread sera détruit avant de revenir de la fonction appelée (autodestruction). Ainsi, quel que soit le travail à faire après "mettre fin au fil" sera exécuté sur un objet mort, et c'est sûr de certitude (probablement un crash dans ce cas). Vous pouvez toujours contrôler votre propre implémentation de la classe pour permettre un
supprimer ceci; code> en sécurité car vous n'accédez à aucun membre de données, mais vous ne pouvez pas "tromper" un objet de classe tiers (
std :: thread code>) se détruire comme ça et s'attendre à ce que les choses soient bien élevées.
Je suggère de supprimer l'acceptation de ma réponse. Il a été accepté alors qu'il est incorrect et même si j'ai essayé de répondre pleinement, je ne suis pas sûr que ce soit totalement correct moi-même.