Défaut, valeur et zéro initialisation

Je suis très confus à propos de l’initialisation value & default-& zero. et surtout quand ils utilisent les différents standards C ++ 03 et C ++ 11 (et C ++ 14 ).

Je cite et essaie de donner une très bonne réponse Value- / Default- / Zero- Init C ++ 98 et C ++ 03 ici pour le rendre plus général car cela aiderait beaucoup d’utilisateurs si quelqu’un pouvait aider à remplir le besoin de lacunes pour avoir un bon aperçu de ce qui se passe quand?

La vue d’ensemble par des exemples en quelques mots:

Parfois, la mémoire renvoyée par le nouvel opérateur sera initialisée, et parfois elle ne le sera pas selon que le type que vous modifiez est un POD (old old data) , ou une classe contenant des membres POD et utilisant un constructeur par défaut généré par le compilateur.

  • En C ++ 1998, il existe 2 types d’initialisation: l’initialisation zéro et l’ initialisation par défaut
  • En C ++ 2003, un troisième type d’initialisation, l’initialisation de la valeur a été ajouté.
  • En C ++ 2011 / C ++ 2014, seule l’ initialisation de liste a été ajoutée et les règles d’ initialisation value- / default- / zero ont été légèrement modifiées.

Assumer:

struct A { int m; }; struct B { ~B(); int m; }; struct C { C() : m(){}; ~C(); int m; }; struct D { D(){}; int m; }; struct E { E() = default; int m;} /** only possible in c++11/14 */ struct F {F(); int m;} F::F() = default; /** only possible in c++11/14 */ 

Dans un compilateur C ++ 98, les éléments suivants doivent se produire :

  • new A – valeur indéterminée ( A est POD)
  • new A() – initialiser à zéro
  • new B – construction par défaut ( B::m est non initialisé, B est non-POD)
  • new B() – construction par défaut ( B::m est non initialisé)
  • new C – construction par défaut ( C::m est initialisé à zéro, C est non-POD)
  • new C() – construction par défaut ( C::m est initialisé à zéro)
  • new D – construction par défaut ( D::m est non initialisé, D est non-POD)
  • new D()construction par défaut? ( D::m n’est pas initialisé)

Dans un compilateur conforme C ++ 03, les choses devraient fonctionner comme ceci:

  • new A – valeur indéterminée ( A est POD)
  • new A() – initialise la valeur A , qui est une initialisation nulle car c’est un POD.
  • new B – initialise par défaut (laisse B::m non initialisé, B est non-POD)
  • new B() – initialise la valeur B qui initialise à zéro tous les champs puisque son moteur par défaut est généré par le compilateur et non par l’utilisateur.
  • new C – initialise par défaut C , qui appelle le ctor par défaut. ( C::m est initialisé à zéro, C est non-POD)
  • new C() – valeur initialise C , qui appelle le ctor par défaut. ( C::m est initialisé à zéro)
  • new D – construction par défaut ( D::m est non initialisé, D est non-POD)
  • new D()initialise la valeur D? , qui appelle le ctor par défaut ( D::m est non initialisé)

Valeurs italiques et? sont des incertitudes, s’il vous plaît aider à corriger cela 🙂

Dans un compilateur conforme C ++ 11, les choses devraient fonctionner comme ceci:

??? (s’il vous plaît aider si je commence ici, il va quand même aller mal)

Dans un compilateur conforme C ++ 14, les choses devraient fonctionner comme ça: ??? (s’il vous plaît aider si je commence ici, il va quand même aller mal) (brouillon basé sur la réponse)

  • new A – initialise par défaut A , compilateur gén. ctor, (quitte A::m non initialisé) ( A est POD)
  • new A() – valeur initialise A , qui est une initialisation nulle depuis 2. pointez sur [dcl.init] / 8

  • new B – initialise par défaut B , compilateur gén. ctor, (leavs B::m non initialisé) ( B est non-POD)

  • new B() – initialise la valeur B qui initialise à zéro tous les champs puisque son moteur par défaut est généré par le compilateur et non par l’utilisateur.
  • new C – initialise par défaut C , qui appelle le ctor par défaut. ( C::m est initialisé à zéro, C est non-POD)
  • new C() – valeur initialise C , qui appelle le ctor par défaut. ( C::m est initialisé à zéro)
  • new D – initialise par défaut D ( D::m n’est pas initialisé, D est non-POD)
  • new D() – initialise la valeur D , qui appelle le ctor par défaut ( D::m n’est pas initialisé)
  • new E – initialise par défaut E , qui appelle le comp. gen. ctor. ( E::m est non initialisé, E est non-POD)
  • new E() – initialise la valeur E , qui initialise E zéro depuis 2 points dans [dcl.init] / 8 )
  • new F – initialise par défaut F , qui appelle le comp. gen. ctor. ( F::m est non initialisé, F est non-POD)
  • new F() – valeur initialise F , qui initialise F par défaut depuis 1. pointe dans [dcl.init] / 8 (la fonction F ctor est fournie par l’utilisateur si elle est déclarée par l’utilisateur et n’est pas explicitement par défaut ou supprimée la première fois déclaration. Lien )

C ++ 14 spécifie l’initialisation des objects créés avec new dans [expr.new] / 17 ([expr.new] / 15 dans C ++ 11, et la note n’était pas une note mais un texte normatif à l’époque):

Une nouvelle expression qui crée un object de type T initialise cet object comme suit:

  • Si le nouvel initialiseur est omis, l’object est initialisé par défaut (8.5). [ Remarque: Si aucune initialisation n’est effectuée, l’object a une valeur indéterminée. – note finale ]
  • Sinon, le nouvel initialiseur est interprété selon les règles d’initialisation de 8.5 pour l’initialisation directe .

L’initialisation par défaut est définie dans [dcl.init] / 7 (/ 6 dans C ++ 11, et le libellé lui-même a le même effet):

Initialiser par défaut un object de type T signifie:

  • si T est un type de classe (éventuellement qualifié cv) (Paragraphe 9), le constructeur par défaut (12.1) pour T est appelé (et l’initialisation est mal formée si T n’a pas de constructeur par défaut ou la résolution de surcharge (13.3) ambiguïté ou dans une fonction qui est supprimée ou inaccessible du contexte de l’initialisation);
  • si T est un type tableau, chaque élément est initialisé par défaut ;
  • sinon, aucune initialisation n’est effectuée.

Ainsi

  • new A provoque uniquement l’appel d’un constructeur par défaut, qui n’initialise pas m . Valeur indéterminée. Devrait être le même pour le new B
  • new A() est interprété selon [dcl.init] / 11 (/ 10 en C ++ 11):

    Un object dont l’initialiseur est un ensemble vide de parenthèses, à savoir () , doit être initialisé en valeur.

    Et maintenant, considérez [dcl.init] / 8 (/ 7 en C ++ 11 †):

    Valoriser-initialiser un object de type T signifie:

    • si T est un type de classe (éventuellement qualifié de cv) (Paragraphe 9) sans constructeur par défaut (12.1) ou avec un constructeur par défaut fourni par l’utilisateur ou supprimé, l’object est initialisé par défaut;
    • si T est un type de classe (éventuellement qualifié de cv) sans constructeur par défaut fourni ou supprimé par l’utilisateur, l’object est initialisé à zéro et les contraintes sémantiques pour l’initialisation par défaut sont vérifiées et si T a un constructeur par défaut non sortingvial , l’object est initialisé par défaut;
    • si T est un type tableau, alors chaque élément est initialisé en valeur;
    • sinon, l’object est initialisé à zéro.

    Par conséquent, le new A() initialisera zéro. Et cela devrait être équivalent pour A et B

  • new C et new C() initialiseront à nouveau l’object par défaut, puisque le premier sharepoint la dernière citation s’applique (C a un constructeur par défaut fourni par l’utilisateur!). Mais, clairement, maintenant m est initialisé dans le constructeur dans les deux cas.


† Eh bien, ce paragraphe a un libellé légèrement différent en C ++ 11, qui ne modifie pas le résultat:

Valoriser-initialiser un object de type T signifie:

  • si T est un type de classe (éventuellement qualifié cv) (Article 9) avec un constructeur fourni par l’utilisateur (12.1), le constructeur par défaut de T est appelé (et l’initialisation est mal formée si T n’a pas de constructeur par défaut accessible) ;
  • Si T est un type de classe non-union (éventuellement qualifié cv) sans constructeur fourni par l’utilisateur, alors l’object est initialisé à zéro et, si le constructeur implicite déclaré par T de T est non sortingvial, ce constructeur est appelé.
  • si T est un type tableau, alors chaque élément est initialisé en valeur;
  • sinon, l’object est initialisé à zéro.

La réponse suivante étend la réponse https://stackoverflow.com/a/620402/977038 qui servirait de référence pour C ++ 98 et C ++ 03

Citer la réponse

  1. En C ++ 1998, il existe 2 types d’initialisation: zéro et par défaut
  2. En C ++ 2003, un troisième type d’initialisation, l’initialisation de la valeur a été ajoutée.

C ++ 11 (en référence à n3242)

Initialiseurs

8.5 Initialiseurs [dcl.init] spécifie qu’une variable POD ou non POD peut être initialisée soit en tant qu’accesseur de braconnage ou d’initialisation égale, soit en tant que braced-init-list ou initializer-clause, en tant que brace-or-equal- initialiseur ou utilisation (liste-expression) . Avant C ++ 11, seule la clause (expression-list) ou initializer-clause était prise en charge, bien que la clause initializer était plus restreinte que celle de C ++ 11. En C ++ 11, initializer-clause prend désormais en charge braced-init-list en dehors de l’ expression d’ affectation comme dans C ++ 03. La grammaire suivante résume la nouvelle clause prise en charge, où la partie en gras est nouvellement ajoutée dans la norme C ++ 11.

initialiseur:
accolade ou égaliseur
(liste d’expressions)
accolade ou égaliseur initial:
= clause d’initialisation
braced-init-list
clause d’initialisation:
expression d’affectation
braced-init-list
liste d’initialisation:
initializer-clause … opt
initializer-list, initializer-clause … opt **
braced-init-list:
{liste d’initialisation, opt}
{}

Initialisation

Comme C ++ 03, C ++ 11 supporte toujours trois formes d’initialisation


Remarque

La partie surlignée en gras a été ajoutée dans C ++ 11 et celle qui a été supprimée a été supprimée de C ++ 11.

  1. Type d’initialiseur: 8.5.5 [dcl.init] _zero-initialize_

Effectué dans les cas suivants

  • Les objects avec une durée de stockage statique ou de threads sont initialisés à zéro
  • S’il y a moins d’initialiseurs que d’éléments de tableau, chaque élément non explicitement initialisé doit être initialisé à zéro
  • Lors de l’initialisation de valeur , si T est un type de classe non-union (éventuellement qualifié de cv) sans constructeur fourni par l’utilisateur, l’object est initialisé à zéro.

Initialiser à zéro un object ou une référence de type T signifie:

  • si T est un type scalaire (3.9), l’object est mis à la valeur 0 (zéro), prise comme expression constante intégrale , convertie en T;
  • si T est un type de classe non-union (éventuellement qualifié cv) , chaque membre de données non statique et chaque sous-object de classe de base sont initialisés à zéro et le remplissage est initialisé à zéro bit;
  • Si T est un type d’union (possiblement qualifié CV) , le premier membre de données nommé non statique de l’object est initialisé à zéro et le remplissage est initialisé à zéro bit;
  • si T est un type tableau, chaque élément est initialisé à zéro;
  • si T est un type de référence, aucune initialisation n’est effectuée.

2. Type d’initialiseur: 8.5.6 [dcl.init] _default-initialize_

Effectué dans les cas suivants

  • Si le nouvel initialiseur est omis, l’object est initialisé par défaut; Si aucune initialisation n’est effectuée, l’object a une valeur indéterminée.
  • Si aucun initialiseur n’est spécifié pour un object, l’object est initialisé par défaut, sauf pour les objects avec une durée de stockage statique ou de thread
  • Lorsqu’une classe de base ou un membre de données non statique n’est pas mentionné dans une liste d’initialisation de constructeur et que ce constructeur est appelé.

Initialiser par défaut un object de type T signifie:

  • si T est un type de classe non POD (éventuellement qualifié cv) (Paragraphe 9), le constructeur par défaut de T est appelé (et l’initialisation est mal formée si T n’a pas de constructeur par défaut accessible);
  • si T est un type tableau, chaque élément est initialisé par défaut;
  • sinon, aucune initialisation n’est effectuée.

Remarque Jusqu’à C ++ 11, seuls les types de classe non POD avec durée de stockage automatique étaient considérés comme initialisés par défaut lorsqu’aucun initialiseur n’est utilisé.


3. Type d’initialiseur: 8.5.7 [dcl.init] _value-initialize_

  1. Lorsqu’un object (temporaire sans nom, variable nommée, durée de stockage dynamic ou membre de données non statique) dont l’initialiseur est un ensemble vide de parenthèses, c’est-à-dire () ou d’accolades {}

Valoriser-initialiser un object de type T signifie:

  • si T est un type de classe (éventuellement qualifié cv) (Article 9) avec un constructeur fourni par l’utilisateur (12.1), le constructeur par défaut de T est appelé (et l’initialisation est mal formée si T n’a pas de constructeur par défaut accessible) ;
  • si T est un type de classe non-union (éventuellement qualifié cv) sans constructeur fourni par l’utilisateur, chaque membre de données et composant de classe de base non statique de T est initialisé en valeur; alors l’object est initialisé à zéro et, si le constructeur par défaut déclaré implicitement par T est non sortingvial, ce constructeur est appelé.
  • si T est un type tableau, alors chaque élément est initialisé en valeur;
  • sinon, l’object est initialisé à zéro.

Donc pour résumer

Remarque La citation pertinente de la norme est surlignée en gras

  • new A: initialise par défaut (laisse A :: m non initialisé)
  • new A (): initialise zéro A, car le candidat initialisé en valeur n’a pas de constructeur par défaut fourni par l’utilisateur ou supprimé. Si T est un type de classe non-union (éventuellement qualifié cv) sans constructeur fourni par l’utilisateur, alors l’object est initialisé à zéro et, si le constructeur par défaut implicite de T est non sortingvial, ce constructeur est appelé.
  • nouveau B: initialise par défaut (laisse B :: m non initialisé)
  • new B (): initialise la valeur B qui initialise à zéro tous les champs; si T est un type de classe (éventuellement qualifié cv) (Article 9) avec un constructeur fourni par l’utilisateur (12.1), le constructeur par défaut de T est appelé
  • new C: default-initialise C, qui appelle le ctor par défaut. Si T est un type de classe (éventuellement qualifié de cv) (Paragraphe 9), le constructeur par défaut de T est appelé . En outre, si le nouvel initialiseur est omis, l’object est initialisé par défaut.
  • new C (): initialise C, qui appelle le ctor par défaut. Si T est un type de classe (éventuellement qualifié cv) (Paragraphe 9) avec un constructeur fourni par l’utilisateur (12.1), le constructeur par défaut de T est appelé. De plus, un object dont l’initialiseur est un ensemble vide de parenthèses, à savoir, (), doit être initialisé en valeur