10
votes

Différence entre l'initialisateur et la liste d'initialiseurs par défaut en C ++

salut, J'ai une question mais je ne trouve pas la réponse pendant une longue période, c'est-à-dire quelle est la différence entre les 2 déclarations suivantes sur le paramètre initialisation? xxx

Je sais qu'il y a "initialisation directe" et "initialisation de copie", mais je Je ne sais pas quelles sont les autres différences et s'il y a une description à propos de la première déclaration?

Merci d'avance


3 commentaires

@ user692270 J'ai modifié et ajouté un colon manquant à votre exemple de liste d'initialisation.


Notez que le = dans le deuxième exemple n'est ni "Initialisation directe" ni "Initialisation de la copie", mais affectation.


Une autre question où User692270 accepte la réponse de @karthik. Arrêtez d'utiliser deux comptes pour l'agriculture upvote.


4 Réponses :


9
votes

Le but de la liste d'initialisation par défaut consiste à initialiser la variable constante avec la classe. Parce que la variable constante est initialisée avant que l'objet soit initialisé.

Je fournis un échantillon pour expliquer la différence entre ces deux initialisations: xxx


2 commentaires

Ce n'est pas seulement pour initialiser les constantes, mais tous les membres . Et c'est obligatoire pour les références aussi.


... ou des membres de type classe et des bases sans constructeurs par défaut.



34
votes

Utilisation de la liste Initializer, les membres sont créés et initialisés une seule fois, avec la valeur donnée.

Utilisation de l'affectation, les membres sont initialisés avec une valeur par défaut, puis réaffectés dans le corps du constructeur .

Dans certains cas (constantes, références) Vous ne pouvez utiliser que des listes d'initialisation car

  • Ceux-ci doivent être initialisés avec une valeur valide et
  • Une fois qu'ils sont construits, les réaffectant est illégal.

    Dans d'autres cas, même si l'attribution est possible, la liste d'initialisation est toujours préférable car elle évite de travail supplémentaire (une double initialisation, qui peut être coûteuse pour certains types) et adhère à un idiome commun, ce qui facilite votre code. comprendre et entretenir.

    Une mise en garde bien à savoir est que l'ordre d'initialisation des membres est défini par leur ordre de déclaration, pas par l'ordre dans lequel ils sont répertorié dans la liste d'initialisation. Par exemple xxx

    Crédit indéfini car B est initialisé avant C , donc avec un valeur non définie. Pour éviter toute confusion et le potentiel de ces bugs subtils, répertoriez toujours les membres de la liste d'initialisation dans le même ordre que celui qu'ils sont déclarés dans la classe.

    Cela peut sembler obscur au début, mais il y a une raison pour ça. En C ++, il est garanti que tous les membres d'une classe sont détruits exactement à l'inverse de l'ordre de création. Maintenant, les classes peuvent avoir plusieurs constructeurs, chacun avec sa propre liste d'initialisation et (malheureusement, on pourrait dire) des listes d'initialisation peuvent être commandées de la manière dont le programmateur souhaite. Si l'ordre de la liste d'initialisation a déterminé l'ordre d'initialisation réel, le temps d'exécution doit en quelque sorte tenir des données sur chaque objet de rappeler quel constructeur qu'il a été créé et dans quel ordre doit être détruit. Cela inciterait les frais généraux d'exécution, sans aucun avantage évident, de sorte que la philosophie générale C ++ de «Pay uniquement pour ce que vous utilisez» - il a été décidé que l'ordre d'initialisation est défini par la commande de déclaration une fois pour toutes.


2 commentaires

Cela devient crucial lorsque l'initialisation est coûteuse ou lorsque vous faites face à des types qui ne sont pas constructibles par défaut.


@Msalters, vous avez raison, peut-être que j'étais trop méticuleux ici. Bien que je ne puisse pas m'empêcher de penser qu'il doit y avoir des gens qui tentent d'essayer d'écrire du code comme c (): ACONST (0) {ACONST = THERELVALUE; } :-)



3
votes

La principale différence est que, dans le premier cas, les éléments de CLAS sont initialisés et dans le second cas, ils sont attribué . Pour les types non intégrés, cela signifie que dans le second cas, vous utiliserez opérateur = pour attribuer des valeurs à vos membres de la classe.

généralement, il est préférable d'utiliser le premier cas, car dans ce cas, les memes de classe sont initialisées avant le corps du constructeur.

En outre, vous ne pouvez pas utiliser l'affectation sur plusieurs cas, par exemple, lorsque le membre de classe est déclaré const . .


0 commentaires

1
votes

pris de Section 10.6 de la FAQ C ++ Par Marshall Cline:

Listes d'initialisation. En réalité, Les constructeurs doivent initialiser en tant que Règle tous les objets membres dans le Liste d'initialisation. Une exception est discuté plus loin.

Considérez le constructeur suivant qui initialise l'objet membre x_ Utilisation d'une liste d'initialisation: Fred :: Fred (): x_ (peu importe) {}. Les avantage le plus courant de faire cela est performance améliorée. Par exemple, si l'expression quel que soit le même Tapez la variable de membre X_, le résultat de l'expression quelle que soit construit directement à l'intérieur de x_ - le compilateur ne fait pas une copie séparée de l'objet. Même si les types sont Pas la même chose, le compilateur est généralement capable de faire un meilleur travail avec Listes d'initialisation qu'avec missions.

l'autre moyen (inefficace) de construire constructeurs est via une mission, telle AS: Fred :: Fred () {x_ = autre chose; }. Dans ce cas, l'expression autre chose provoque un objet séparé et temporaire à être créé et cet objet temporaire est transmis dans l'objet x_ opérateur d'assignation. Alors que objet temporaire est détruit à la ;. C'est inefficace.

comme si cela n'était pas assez mauvais, il y a une autre source d'inefficacité lorsque Utilisation de l'affectation dans un constructeur: le L'objet membre sera pleinement construit par sa valeur par défaut constructeur, et cela pourrait, pour exemple, allouer un montant par défaut de mémoire ou ouvrez certains fichiers par défaut. Tout ce travail pourrait être pour rien que l'expression et / ou / ou L'opérateur d'affectation provoque l'objet fermer ce fichier et / ou libérer ce Mémoire (par exemple, si la valeur par défaut constructeur n'a pas alloué un grand assez de piscine de mémoire ou si elle a ouvert le mauvais fichier).

Conclusion: Toutes les autres choses étant égal, votre code fonctionnera plus vite si Vous utilisez plutôt des listes d'initialisation que l'affectation.

Remarque: il n'y a pas de performance différence si le type de x_ est un peu type intégré / intrinsèque, tel que INT ou char * ou float. Mais même dans ces cas, ma préférence personnelle est de Définissez ces membres de données dans le liste d'initialisation plutôt que via affectation pour la cohérence. Un autre argument de symétrie en faveur de l'utilisation de Listes d'initialisation même pour Types intégrés / intrinsèques: non statique Données de référence Const et non statiques Les membres ne peuvent pas être attribués une valeur dans le constructeur, donc pour la symétrie a du sens d'initialiser tout Dans la liste d'initialisation.

Maintenant pour les exceptions. Chaque règle a exceptions (hmmm; fait "chaque règle a exceptions "ont des exceptions? rappelle moi de l'incomplétude de Gödel Théorèmes), et il y a deux exceptions à "l'initialisation de l'utilisation Listes "règle. La ligne de fond est à utiliser Bon sens: si c'est moins cher, mieux, plus rapide, etc. pour ne pas les utiliser, puis par Tous les moyens ne les utilisent pas. Cela pourrait arriver quand votre classe a deux constructeurs qui doivent initialiser les membres de données de ce objet dans ordres différents. Ou cela pourrait arriver Quand deux membres de données sont auto-référentiel. Ou quand un Les données-membres ont besoin d'une référence à la Cet objet, et vous voulez éviter un Avertissement du compilateur sur l'utilisation de la ceci mot clé avant le {qui commence la corps du constructeur (quand votre Un compilateur particulier arrive à émettre cet avertissement particulier). Ou quand vous besoin de faire un test si / lancer sur un Variable (paramètre, global, etc.) avant d'utiliser cette variable pour initialiser l'un de vos membres. Cette liste n'est pas exhaustive; s'il te plaît Ne m'écris pas me demandant d'ajouter un autre "ou quand ...". Le point est Simplement ceci: utiliser le bon sens.


1 commentaires

Une bonne réponse, pour être sûr, mais peut-être un peu ... long.