J'ai la classe Stack suivante.
Stack(const Stack& s) : size(s.size), x(new int[size]) {}
Notez l'affectation dans le constructeur de copie. Le code fonctionne, compile correctement et le compilateur (gcc) ne se plaint même pas avec les indicateurs -Wall -Wextra
. Est-ce que le compilateur réécrit automatiquement le compilateur à ceci?
class Stack{ public: int size; int* x; Stack() : size(10), x(new int[10]) {} Stack(const Stack& s) : x(new int[size=s.size]) {} };
Ou y a-t-il une autre magie? J'ai remarqué que lorsque je change l'ordre des définitions, le compilateur se plaint d'une initialisation non dans l'ordre. Donc je suppose que c'est le cas que j'ai mentionné. Je n'ai rien trouvé dans la documentation et la sortie ASM ne m'aide pas non plus.
4 Réponses :
Depuis la page Cppreference.com sur constructeurs :
Les noms qui apparaissent dans la liste d'expressions ou d'accolade-init-list sont évalués dans la portée du constructeur:
Alors oui, size
fait ici référence à this-> size
dans le périmètre du constructeur, et à l'affectation size = s.size
est une expression valide.
Il va sans dire que vous ne devriez pas vous attendre à ce que cela passe une révision du code :)
Les membres d'une classe sont toujours initialisés dans l'ordre de déclaration, quel que soit l'ordre dans lequel vous les spécifiez dans la liste d'initialisation des membres. Et si vous les omettez, ils sont initialisés par défaut. Donc faire ceci:
Stack(const Stack& s) : x(new int[size=s.size]) {}
Signifie que la taille
est initialisée par défaut en premier. Cela lui laisse une valeur indéterminée (car les types fondamentaux sont supposés être initialisés par défaut). Ensuite, l'initialiseur pour x
est évalué. Une partie de l'évaluation de new int [size = s.size]
implique l'expression d'affectation size = s.size
, qui modifie size
en tant qu'effet secondaire . Donc, votre code est correct ici, bien qu'il soit susceptible de soulever des sourcils.
Lorsque vous changez l'ordre des membres, l'attribution se produit avant taille code > est censé être initialisé. Cela laisse votre code ouvert à un comportement indéfini.
Êtes-vous sûr que l’affectation doit avoir lieu après l’initialisation par défaut? (Est-il séquencé après ?)
@MartinBonner - Je me souviens vaguement de l'avoir lu une fois. Laissez-moi vérifier.
@MartinBonner C'est vrai. il y a une note ici , et je pouvez trouver la clause réelle qui s'applique si vous en avez vraiment besoin.
@MartinBonner - Couple timsong-cpp.github.io/cppwp/intro.execution# 5.4 avec timsong-cpp.github.io/cppwp/intro. exécution # 9 et considérez que l'intiialisation se déroule dans l'ordre des déclarations. Alors, oui, je suis sûr maintenant.
Le compilateur réécrit-il automatiquement le compilateur avec cela?
Stack(const Stack& s) : size(s.size), x(new int[s.size]) {}No.
Stack(const Stack& s) : size(), x(new int[size=s.size]) {}peut être considéré comme étant
Stack(const Stack& s) : x(new int[size=s.size]) {}mais ce n'est pas vraiment depuis l'écriture
size ()
valoriserait l'initialiser, ce qui signifie qu'il est initialisé à zéro, mais comme c'est le compilateur qui synthétise l'initialisation, l'initialisation par défaut [1] se produit, ce qui signifie qu'il n'est pas initialisé. Ensuite, vous lui attribuez une valeur lors de l'initialisation dex
. C'est "sûr", ce qui signifie que cela fonctionne dans ce cas, mais je ne le recommanderais pas.Stack(const Stack& s) : size(s.size), x(new int[size]) {}initialise les deux membres et si jamais vous changez leur ordre dans la classe, vous aurez toujours le bon comportement.
Je dois aimer un langage de programmation où l'initialisation nécessite une thèse entière, hein? :) Désolé pour pinailler, mais c'est un terrain dangereux où l'OP est de toute façon.
@StoryTeller Pas de soucis. Je préfère que la nit soit choisie car elle est très loin dans le terrier du lapin :)
Non, il le réécrit comme suit:
class Stack{ public: int size; int* x; Stack() : size(10), x(new int[10]) {} Stack(const Stack& s) :size(), x(new int[size=s.size]) {} };
size ()
serait un constructeur par défaut pour l'objet size
mais int s n'en a pas, donc c'est juste une instruction vide et size
reste non initialisé. Ou est-ce le cas?
Je dirais que ce code peut générer un comportement indéfini. Les variables membres sont initialisées dans l'ordre dans lequel elles sont déclarées selon 10.9.2 Initialisation bases et membres . Mais l'ordre d'évaluation des expressions utilisées pour l'initialisation n'est pas défini. Ainsi, size = s.size
peut être appelé avant ou après size ()
. Pour les types de classe, cela poserait un problème, mais je ne suis pas sûr que size ()
soit garanti sans opération et si le compilateur pourrait décider d'initialiser la variable par ex. 0.
Voir les commentaires sous la réponse de Story Teller. Ce n'est pas un comportement indéfini (mais très malodorant de toute façon).
@MartinBonner Oh, je ne l'ai pas vu en écrivant ma réponse. Je vous remercie.