8
votes

Question de portée variable locale

Pourquoi le code suivant imprime-t-il "xxy"? Les variables locales ne devraient-elles pas vivre dans la portée de la fonction complète? Puis-je utiliser un tel comportement ou cela sera modifié dans la norme de la vie future C ++?

Je pensais que selon C ++ standard 3.3.2 " un nom déclaré dans un bloc est local à ce bloc. Sa portée potentielle commence à sa point de déclaration et se termine à la fin de sa région déclarative. " xxx


basé sur les réponses que je peux supposer que myclass (12345 ); est l'expression (et la portée). C'est un sens. Je m'attends donc à ce que le code suivant imprime "xyx" toujours: xxx

et il est autorisé à effectuer un tel remplacement: xxx


3 commentaires

Votre question contient la réponse: un nom déclaré ... . Il n'y a pas de nom!


Dans cet exemple: MyClass (12345) est une forme de style de fonction, pas une déclaration.


Néanmoins, il n'y a pas de nom pour l'instance


4 Réponses :


8
votes

Vous créez réellement un objet sans le maintenir dans la portée, il est donc détruit juste après sa création. D'où le comportement que vous ressentez.

Vous ne pouvez pas accéder à l'objet créé, alors pourquoi le compilateur le garderait-il?


0 commentaires

16
votes

L'objet créé dans votre xxx

est un objet temporaire qui n'est que vivant dans cette expression ; xxx

est un objet qui est vivant pour tout le bloc.


10 commentaires

N'est pas cette expression est la fonction principale?


Cela me semble juste. Une autre chose pourrait être une optimisation: même si vous utilisez la deuxième méthode, le compilateur peut l'optimiser à la première.


@Anna, dans le second cas, il imprimera toujours xyx.


@Kirill: Cette expression est dans une déclaration dans principale () . Ce n'est pas l'ensemble de principal () .


@Anna: C'est vrai. Les spécifications parlent de "périmètre potentiel", la portée n'est donc pas appliquée. Le compilateur peut faire ce qu'il veut (y compris détruire l'objet tôt)


@Kirill: Notez mon utilisation du mot "temporaire". Cette façon de créer des objets est utile lors de la création d'un objet dans le seul but de les transmettre à une fonction: FOO (MyClass (12345)), auquel cas l'objet existera pour la durée de l'appel de fonction puis détruit.


@Jespire, c'est vrai de les transmettre à une fonction, car l'appel de la fonction sera une expression. Mais ici, je ne vois que créer un objet temporaire. Où expression est?


@Kirill: Je ne vois pas le problème. Le comportement est totalement attendu. Les spécifications parlent d'un nom en cours de création, ce qui n'est pas le cas dans votre pièce de code (vous n'abandonnez pas une variable).


@Philippe, compilateur ne peut détruire l'objet tôt dans le second cas. Dans ce cas en utilisant boost :: scoped_lock sera impossible.


@Kirill: La chaîne est "myClass (12345)" est l'expression.



4
votes

Vous avez cité standard correctement. Permettez-moi de souligner:

A nom déclaré dans un bloc est local à ce bloc. Sa portée potentielle commence à son point de déclaration et se termine à la fin de sa région déclarative.

Vous n'avez pas déclaré aucun nom , en fait. Votre ligne xxx

ne contient même pas de déclaration ! Ce qu'il contient est une expression qui crée une instance de myClass, calcule l'expression (toutefois, dans ce cas particulier, il n'y a rien à calculer) et jette son résultat à vide et détruit les objets créés là-bas.

Une chose moins confuse semblerait comme xxx

Vous l'avez vu plusieurs fois et savoir comment ça fonctionne, n'est-ce pas? < / p>


2 commentaires

Les spécifications parlent également de la portée "potentielle". Pas garantie". Donc, le compilateur peut détruire l'objet plus tôt s'il n'est pas utilisé dans la portée.


"Scope potentiel" a une signification précise: c'est la portée plus les pièces où le nom est caché en raison de la ré-déclaration. Une implémentation ne peut pas détruire un objet plus tôt, même s'il n'est plus utilisé (cela allait une utilisation majeure de RAII BTW).



5
votes

Pour répondre à vos autres questions. Ce qui suit est l'invocation de l'opérateur de la virgule. Il crée un myClass temporaire, qui inclut l'appel de son constructeur. Il évalue ensuite la deuxième expression COUT << "Y" << endl qui imprimera le Y. Il puis, à la fin de l'expression complète, détruira le temporaire, qui appellera son destructeur . Donc, vos attentes étaient correctes. XXX

Pour ce qui suit fonctionne, vous devez ajouter des parenthèses, car la virgule a une signification prédéfinie dans les déclarations. Il commencerait à déclarer une fonction quelque_func renvoyer un int et ne prenant aucun paramètre et affecterait l'objet scoped_lock à x . En utilisant des parenthèses, vous dites que tout est une seule expression de l'opérateur de virgule. xxx

Il convient de noter que les deux lignes suivantes sont équivalentes. Le premier fait pas créer un objet temporaire sans nom à l'aide de my_mutex comme argument du constructeur, mais les parenthèses autour du nom sont redondants. Ne laissez pas la syntaxe vous confondre. xxx


J'ai vu une mauvaise utilisation des termes de la portée et de la vie.

  • Scope est où vous pouvez vous référer à un nom sans qualifier son nom. Les noms ont des étendues et des objets héritent de la portée du nom utilisé pour les définir (donc parfois la norme indique "objet local"). Un objet temporaire n'a aucune portée, car il n'a aucun nom. De même, un objet créé par nouveau n'a aucune portée. La portée est une propriété de la compilation. Ce terme est fréquemment mal utilisé dans la norme, voir Ce rapport de défaut , donc il est assez déroutant de trouver une vraie signification.

  • Lifetime est une propriété d'exécution. Cela signifie que lorsque l'objet est configuré et prêt à être utilisé. Pour un objet de type de classe, la durée de vie commence lorsque le constructeur met fin à l'exécution, et il se termine lorsque le destructeur commence l'exécution. La durée de vie est souvent confondue avec la portée, bien que ces deux choses soient complètement différentes.

    La durée de vie des temporaires est définie avec précision. La plupart d'entre eux terminent la durée de vie après évaluation de l'expression complète qu'ils figurent dans (comme, l'exploitant des virgules ci-dessus ou une expression d'affectation). Les temporaires peuvent être liés à des références constantes qui allongeront leur vie. Les objets étant lancés dans des exceptions sont des temporaires également et leur vie se termine quand il n'y a plus de gestionnaire pour eux.


0 commentaires