2009-04-21 4 views
8

Disons que j'ai un struct défini comme:Qu'est-ce que cela signifie d'instancier un objet en utilisant des accolades en C++?

typedef 
struct number{ 
    int areaCode; 
    int prefix; 
    int suffix; 
} PhoneNumber; 

Lorsque je crée une instance de cette structure, si j'utilise la syntaxe suivante:

PhoneNumber homePhone = {858, 555, 1234}; 

... quel constructeur est-il appeler? Le constructeur par défaut, ou le constructeur de la copie, ou pas du tout parce qu'il n'appelle pas 'new'?

Le vrai but de cette question est de comprendre comment je peux ajouter un quatrième champ. Je tiens donc à redéfinir mon struct comme:

typedef 
struct number{ 
    int areaCode; 
    int prefix; 
    int suffix; 
    int extension; // NEW FIELD INTRODUCED 
} PhoneNumber; 

Alors maintenant, je peux créer de nouveaux objets PhoneNumber avec quatre champs:

PhoneNumber officePhone = {858, 555, 6789, 777} 

Cependant, j'ai des centaines de ces instances PhoneNumber déjà créées avec seulement 3 champs (xxx, xxx, xxxx). Donc, je ne veux pas passer en revue et modifier chaque instanciation unique de mon objet PhoneNumber qui est déjà défini. Je veux être en mesure de laisser ces seuls, mais toujours être en mesure de créer de nouvelles instances de numéros de téléphone avec quatre champs. J'essaie donc de comprendre comment je peux écraser le constructeur pour que mes instanciations à trois paramètres ne se brisent pas, mais il supportera également mes nouvelles instanciations à quatre paramètres. Lorsque j'essaie de définir un constructeur par défaut qui prend trois champs et place le quatrième à la valeur par défaut '0', j'obtiens des erreurs (dans la partie d'instanciation du code, pas la définition du constructeur) se plaignant que mon objet doit être initialisé par le constructeur, pas par {...}. Il semble donc que si je remplace le constructeur par défaut, je ne peux plus utiliser d'accolades pour créer mes nouveaux objets?

Désolé si cela s'éloigne complètement des questions d'origine.

+0

Connexe: [http://stackoverflow.com/questions/88957/what-does-0-mean-in/c/88960#88960](http://stackoverflow.com/questions/ 88957/what-does-0-mean-in-c/88960 # 88960) –

Répondre

1

Il appelle effectivement le ctor par défaut; ce qui se passe est une structure est allouée, et chaque valeur est affectée utilisée par défaut "=".

+0

Le constructeur par défaut n'est pas appelé, et operator = n'est pas appelé non plus. En C++ 8.5.1, il définit cette syntaxe pour l'initialisation des agrégats. –

+0

Avez-vous * lu * ce que j'ai écrit? Quelle est la valeur par défaut "="? –

2

Une structure en C++ est comme une classe. Le constructeur par défaut est appelé. Ensuite, chaque champ est copié avec son opérateur d'affectation.

+0

Le constructeur par défaut n'est pas appelé pour les types POD et les champs ne sont pas copiés. La syntaxe définit l'initialisation des membres de l'agrégat. Voir C++ 8.5.1 –

4

Les membres sont en réalité copie-initialisée. Le constructeur par défaut pour chacun n'est pas appelé et aucun operator= n'est impliqué contrairement à ce que suggèrent d'autres réponses. Il peut être montré par un logiciel appelé geordi - alternativement en lisant la norme. Je vais montrer le "fun" façon d'utiliser ce logiciel. Il a une classe tracked::B qui peut nous montrer quand les constructeurs/constructeurs de copie ou les destructeurs/opérateurs d'assignation de copie sont appelés. La sortie il montre est (TRACK limites de suivi à l'instruction suivant celle-ci):

B1*(B0) B1~ 

je ce code

struct T { tracked::B b; }; int main() { tracked::B b; TRACK T t = { b }; } 

Comme vous le voyez, le deuxième objet B - qui est le membre au sein de la section locale La variable t est une copie initialisée à partir de l'autre objet b. Bien sûr, aucun opérateur d'affectation n'est activé. Vous pouvez lire à ce sujet dans 12.6.1/2 dans la norme, si vous le souhaitez.

La même chose est vraie pour les tableaux (qui sont également des agrégats). Beaucoup de gens croient que les objets qui sont membres de tableaux doivent avoir un type qui a un constructeur par défaut. Mais ce n'est pas vrai. Ils peuvent simplement être copiés par un autre objet de leur type et cela fonctionnera bien.

Tous les autres éléments qui n'étaient pas explicitement initialisés dans l'agrégat sont initialisés. L'initialisation de la valeur est un mélange d'initialisation par défaut et d'initialisation zéro. En fait, si un membre a un type qui a un constructeur déclaré par l'utilisateur, alors ce constructeur est appelé. S'il a un type qui a et non un constructeur déclaré par l'utilisateur, alors chaque membre de celui-ci est initialisé. Pour les types intégrés (int, bool, pointeurs, ...) une initialisation de valeur est identique à l'initialisation zéro (cela signifie qu'une telle variable deviendra zéro). Ce qui suit initialiser chaque membre à zéro - à l'exception du premier (a), qui sera l'une:

struct T { int a, b, c; }; int main() { T t = { 1 }; } 

Ces règles d'initialisation sont effrayants, en effet - en particulier parce que la révision de 2003 de C++ introduit que l'initialisation de la valeur. Il wasn't part of the Standard à partir de 1998. Si vous êtes plus intéressé par ces initialisations entourées accolade, vous pouvez lire How to initialize nested structures in C++?.

3

Il n'appelle pas le ctor par défaut, comme d'autres l'ont écrit. Conceptuellement, c'est la même chose, mais en pratique, vous ne trouverez aucun appel de fonction dans le code d'assemblage. Au lieu de cela, les membres restent non initialisés; vous les initialisez avec la construction bouclée-accolade.

Il est intéressant de ceci:

PhoneNumber homePhone = {858, 555, 1234}; 

résultats dans cette assemblée (GCC 4.0.1, -O0):

movl $858, -20(%ebp) 
movl $555, -16(%ebp) 
movl $1234, -12(%ebp) 

Pas beaucoup de surprises là-bas. L'assembly est la fonction contenant l'instruction C++ ci-dessus. Les valeurs (commençant par $) sont déplacées (movl) en décalages dans la pile (registre ebp). Ils sont négatifs car les emplacements de mémoire des membres de structure précèdent le code d'initialisation.

Si vous n'initialisez pas complètement la struct, laissez-à-dire quelques membres comme ceci:

PhoneNumber homePhone = {858, 555}; 

... je reçois le code assembleur suivant:

movl $0, -20(%ebp) 
movl $0, -16(%ebp) 
movl $0, -12(%ebp) 
movl $858, -20(%ebp) 
movl $555, -16(%ebp) 

semble que le compilateur fait alors quelque chose de très similaire à l'appel du constructeur par défaut, suivi par l'affectation. Mais encore une fois, c'est en ligne dans la fonction d'appel, pas un appel de fonction.

Si d'autre part vous définissez un constructeur par défaut qui initialise les membres, aux valeurs données, comme ceci:

struct PhoneNumber { 
    PhoneNumber() 
    : areaCode(858) 
    , prefix(555) 
    , suffix(1234) 
    { 
    } 

    int areaCode; 
    int prefix; 
    int suffix; 
}; 

PhoneNumber homePhone; 

Ensuite, vous obtenez le code assembleur qui appelle en fait une fonction, et initialise les membres de données par l'intermédiaire d'un pointeur vers la structure:

movl 8(%ebp), %eax 
movl $858, (%eax) 
movl 8(%ebp), %eax 
movl $555, 4(%eax) 
movl 8(%ebp), %eax 
movl $1234, 8(%eax) 

Chaque ligne qui va movl 8(%ebp), %eax définit la valeur du pointeur (de registre EAX) au début des données de la structure. Dans les autres lignes, eax est utilisé directement, avec un décalage de 4 et un décalage de 8, similaire à l'adressage direct de la pile dans les deux exemples précédents.

Bien sûr, tout cela est spécifique à l'implémentation du compilateur, mais je serais surpris si d'autres compilateurs ont fait quelque chose d'extraordinairement différent.

+0

L'utilisation du constructeur par défaut avec la liste d'initialisation des membres génère-t-elle un déréférencement de pointeur si l'optimisation est augmentée? C'est à dire. avec gcc, compiler avec l'option -O3 supprime-t-il les déréférences du pointeur? – paxos1977

+0

Regarder la sortie du compilateur ne remplace pas la compréhension de la spécification. –

+0

@ceretullis: Je n'ai pas regardé. @ don.neufeld: d'accord, mais cela ne veut pas dire qu'il est inutile de comprendre ce qui se passe réellement. –

0

Cependant, j'ai des centaines de ces instances PhoneNumber déjà créés avec seulement trois champs (xxx, xxx, xxxx). Donc je ne veux pas passer à travers et modifier CHAQUE instanciation unique de mon objet PhoneNumber qui est déjà défini.

Pas de problème, il suffit d'appeler update-instance-for-redefined-class et ..... er, nevermind. Procéder pour marquer ce "inutile"

2

Initialisation est, au moins pour moi, l'une des parties les plus difficiles de la norme C++. La syntaxe que vous utilisez: Aggregate x = { 1, 2, 3, 4 }; définit l'initialisation d'un type d'agrégat avec les quatre premiers membres assignés valeurs 1, 2, 3 et 4.

Comme une note à votre cas particulier (la structure est passée de 3 à 4 éléments), les champs non présents dans la liste d'initialisation entre les accolades seront valeur initialisée, ce qui en particulier pour les types scalaires est équivalent à 0 initialisation qui est lui-même la même que l'affectation de 0. Ainsi, votre quatrième élément sera initialisé à 0.

Références

Tout est défini dans la norme C++, chapitre 8.5, et plus précisément dans 8.5.1 Agrégats. Les agrégats ne seront pas initialisés avec un constructeur par défaut implicitement déclaré. Si vous utilisez la syntaxe ci-dessus, vous demandez au compilateur d'initialiser les champs donnés avec les valeurs fournies. Tous les champs supplémentaires dans l'ensemble est valeur initialisée

Maintenant, l'initialisation valeur est définie comme un appel au constructeur par défaut défini par l'utilisateur si le type est une classe avec un tel constructeur. S'il s'agit d'un tableau ou d'une classe non-union sans constructeur défini par l'utilisateur, alors chacun des attributs membres sera valeur initialisée. Sinon, l'objet sera zéro initialisées, qui a ensuite à nouveau défini comme ...

zéro initialisation est définie comme réglage de la valeur à 0 pour les types scalaires. Pour les classes, chaque membre de classe et chaque classe de base sera zéro initialisé.Pour les syndicats, le premier membre (uniquement) sera zéro initialisé, pour les tableaux tous les membres seront zéro initialisé.

0

Aucun constructeur n'est concerné par votre type. La syntaxe existante à trois paramètres doit donc être modifiée au niveau du site d'initialisation agrégé.

À moins que la sémantique de votre champ nouvellement ajouté à savoir. ce nouveau type de champ, sont 'remis à zéro-aware conception' (langage recommandé par la programmation .NET, Brad et Co etc dans les lignes directrices de conception non-sens), vous

ne peut pas:

a) offrir quelque chose de plus significatif que ce soit C++ paramètres par défaut si avec un peu de magie il y avait une méthode impliquée (Deault/option params sera bientôt disponible dans le marché de masse boutique C# près du magasin d'alimentation web 4.0 et ancienne COM)

et vous ne pouvez

b) suivre le motif C# de la conception de types de valeur avec des marqueurs de valeur invalides comme 0 (qui est dans ce cas sans doute v mauvais et mauvais en général si vous n'avez pas le contrôle sur les constantes - quelque chose que les bibliothèques au niveau de la source font très bien et tous les frameworks de type JavaTM MS aspirent à). En résumé, si 0 est une valeur valide, vous êtes bourré et quelque chose que la plupart des rédacteurs du compilateur considèrent comme utile avant qu'un style managé ou un idiome n'ait jamais existé; mais ce n'est pas forcément juste.

Quoi qu'il en soit, votre meilleure chance n'est pas C++, ou C#. C'est la génération de code, c'est à dire. méta-programmation plus libérale que le modèle de C++ moderne pousse sur .. Vous le trouverez semblable à l'annotation de tableau de JSON et pourriez employer l'esprit ou (mon conseil serait) rouler vos propres utils. Cela aide à long terme, et finalement vous voudrez aller mieux (c'est à dire ce qu'ils appellent la modélisation de lame-man aka Oslo dans .NET). [Des problèmes comme celui-là sont partout dans beaucoup de langues, c'est un inconvénient de tous ceux de style C (C, C++, Java, C#, vous l'appelez); très semblable aux tableaux de hackery de tableaux requis pour tout type d'E/S inter-domaines ou sémantique et évident même dans web-tech comme SOAP etc .. nouveaux bits variadiques C++ 0x ne contribuent pas beaucoup ici non plus, mais apparemment apporter des temps de compilation exponentiellement plus rapides si vous avez choisi de jouer avec un hackery modèle. Qui s'en soucie :)]

Questions connexes