web-dev-qa-db-fra.com

Défaut d'initialisation par défaut, valeur et zéro

Je suis très confus au sujet de l'initialisation de la valeur, de la valeur par défaut et de zéro. et surtout quand ils entrent en jeu pour les différentes normes C++ 03 et C++ 11 (et C++ 14 ).

Je cite et j'essaie d'étendre 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 à combler les lacunes nécessaires pour avoir un bon aperçu de ce qui se passe quand?

L'aperçu complet par des exemples en bref:

Parfois, la mémoire retournée par le nouvel opérateur sera initialisée, et parfois cela ne dépendra pas si le type que vous créez est un POD (plain old data) , ou s'il s'agit d'une classe qui contient des membres POD et utilise un constructeur par défaut généré par le compilateur.

  • Dans C++ 1998 , il existe 2 types d'initialisation: zéro - et initialisation par défaut
  • Dans C++ 2003 un 3ème type d'initialisation, initialisation de valeur a été ajouté.
  • Dans C++ 2011/C++ 2014 seulement l'initialisation de la liste a été ajoutée et les règles pour valeur-/défaut-/initialisation zéro a changé un peu.

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 événements suivants doivent se produire :

  • new A - valeur indéterminée (A est POD)
  • new A()- initialisation zéro
  • new B - construction par défaut (B::m N'est pas initialisé, B n'est pas POD)
  • new B() - construction par défaut (B::m n'est pas initialisé)
  • new C - construction par défaut (C::m Est initialisé à zéro, C n'est pas POD)
  • new C() - construction par défaut (C::m est initialisé à zéro)
  • new D - construction par défaut (D::m N'est pas initialisé, D n'est pas POD)
  • new D() - construction par défaut? (D::m n'est pas initialisé)

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

  • new A - valeur indéterminée (A est POD)
  • new A() - value-initialize A, qui est une initialisation nulle puisqu'il s'agit d'un POD.
  • new B - initialise par défaut (laisse B::m Non initialisé, B n'est pas POD)
  • new B() - valeur-initialise B qui initialise à zéro tous les champs puisque son ctor par défaut est généré par le compilateur et non défini par l'utilisateur.
  • new C - initialise par défaut C, qui appelle le ctor par défaut. (C::m Est initialisé à zéro, C n'est pas 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 N'est pas initialisé, D n'est pas POD)
  • new D() - valeur initialise D? , qui appelle le ctor par défaut (D::m n'est pas initialisé)

Valeurs italiques et? sont des incertitudes, veuillez aider à corriger ceci :-)

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

??? (s'il vous plaît, aidez-moi si je commence ici, ça ira de toute façon)

Dans un compilateur conforme C++ 14, les choses devraient fonctionner comme ceci: ??? (aidez-moi si je commence ici, ça ira quand même mal) (Brouillon basé sur la réponse)

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

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

  • new B() - valeur-initialise B qui initialise à zéro tous les champs puisque son ctor par défaut est généré par le compilateur et non défini par l'utilisateur.
  • new C - initialise par défaut C, qui appelle le ctor par défaut. (C::m Est initialisé à zéro, C n'est pas 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 n'est pas POD)
  • new D() - valeur initialise D, qui appelle le ctor par défaut (D::m n'est pas initialisé)
  • new E - initialise par défaut E, qui appelle la maquette. gen. ctor. (E::m N'est pas initialisé, E n'est pas POD)
  • new E() - valeur initialise E, qui initialise zéro E depuis 2 points dans [dcl.init]/8 )
  • new F - initialise par défaut F, qui appelle la maquette. gen. ctor. (F::m N'est pas initialisé, F n'est pas POD)
  • new F() - valeur-initialise F, qui initialise par défaut F depuis 1. point in [dcl.init]/8 (F la fonction ctor est fournie par l'utilisateur si elle est déclarée par l'utilisateur et non explicitement par défaut ou supprimée lors de sa première déclaration. Lien )
80
Gabriel

C++ 14 spécifie l'initialisation des objets 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 de retour puis):

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

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

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

Pour initialiser par défaut un objet de type T signifie:

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

Ainsi

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

    Un objet dont l'initialiseur est un ensemble de parenthèses vide, c'est-à-dire (), Doit être initialisé en valeur.

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

    Pour initialiser une valeur un objet de type T signifie:

    • si T est un type de classe (éventuellement qualifié cv) (article 9) sans constructeur par défaut (12.1) ou un constructeur par défaut fourni ou supprimé par l'utilisateur, alors l'objet est initialisé par défaut;
    • si T est un type de classe (éventuellement qualifié par cv) sans constructeur par défaut fourni par l'utilisateur ou supprimé, alors l'objet 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 trivial, l'objet est initialisé par défaut;
    • si T est un type de tableau, alors chaque élément est initialisé en valeur;
    • sinon, l'objet est initialisé à zéro.

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

  • new C Et new C() initialiseront par défaut l'objet à nouveau, puisque le premier point de 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 une formulation légèrement différente en C++ 11, ce qui ne modifie pas le résultat:

Pour initialiser une valeur un objet de type T signifie:

  • si T est un type de classe (éventuellement qualifié par cv) (article 9) avec un constructeur fourni par l'utilisateur (12.1), alors le constructeur par défaut pour T est appelé (et l'initialisation est mauvaise -formé si T n'a pas de constructeur par défaut accessible);
  • si T est un type de classe non-union (éventuellement qualifié par cv) sans constructeur fourni par l'utilisateur, alors l'objet est initialisé à zéro et, si T est le constructeur par défaut implicitement déclaré n'est pas trivial, ce constructeur est appelé.
  • si T est un type de tableau, alors chaque élément est initialisé en valeur;
  • sinon, l'objet est initialisé à zéro.
24
Columbo

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

Citant la réponse

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

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

Initialiseurs

8.5 Initialiseurs [dcl.init] spécifie qu'un variable POD ou non POD peut être initialisé soit brace-or-equal-initializer qui peut être braced-init-list ou clause-initializer globalement appelé accolade-ou-égal-initialiseur ou en utilisant (liste-d'expression). Avant C++ 11, seulement (expression-list) ou initializer-clause était pris en charge si initializer-clause était plus restreint que ce que nous avons en C++ 11. En C++ 11, clause initializer prend désormais en charge braced-init-list à part affectation-expression comme c'était le cas en 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:
initialiseur accolade ou égal
(Liste d'expressions)
initialiseur accolade ou égal:
= Clause-initialiseur
braced-init-list
clause d'initialisation:
& Nbspassignment-expression
braced-init-list
liste d'initialiseurs:
clause d'initialisation ... opt
liste-initialiseur, clause-initialiseur ... opt **
braced-init-list:
{Liste-initialiseur, opt}
{}

Initialisation

Comme C++ 03, C++ 11 prend toujours en charge trois formes d'initialisation


Remarque

La partie mise en évidence en gras a été ajoutée en C++ 11 et celle qui est barrée a été supprimée de C++ 11.

  1. Type d'initialisation: 8.5.5 [dcl.init] _zero-initialize_

Effectué dans les cas suivants

  • Les objets avec une durée de stockage statique ou de thread 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
  • Pendant value-initialize, si T est un type de classe non-union (éventuellement qualifié par cv) sans constructeur fourni par l'utilisateur, alors l'objet est initialisé à zéro.

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

  • si T est un type scalaire (3.9), l'objet est mis à la valeur 0 (zéro), pris comme une expression constante intégrale, converti en T;
  • si T est un (éventuellement qualifié cv) type de classe non union, chaque membre de données non statique et chaque sous-objet de classe de base est initialisé à zéro et le remplissage est initialisé à zéro bits;
  • si T est un (éventuellement qualifié cv) type d'union, le premier membre de données nommé non statique de l'objet est zéro initialisé et le remplissage est initialisé à zéro bits;
  • si T est un type de 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'initialisation: 8.5.6 [dcl.init] _default-initialize_

Réalisé dans les cas suivants

  • Si le nouvel initialiseur est omis, l'objet est initialisé par défaut; si aucune initialisation n'est effectuée, l'objet a une valeur indéterminée.
  • Si aucun initialiseur n'est spécifié pour un objet, l'objet est initialisé par défaut, sauf pour les objets 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 objet de type T signifie:

  • si T est un (éventuellement qualifié cv) non POD type de classe (article 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 de 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'initialisation: 8.5.7 [dcl.init] _value-initialize_

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

Pour initialiser en valeur un objet de type T, cela signifie:

  • si T est un type de classe (éventuellement qualifié cv) (article 9) avec un constructeur fourni par l'utilisateur ( 12.1), alors le constructeur par défaut pour 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é par cv) sans constructeur fourni par l'utilisateur, alors chaque membre de données non statique et composant de classe de base de T est initialisé en valeur;  alors l'objet est initialisé à zéro et, si le constructeur par défaut implicitement déclaré de T est non trivial, ce constructeur est appelé.
  • si T est un type de tableau, alors chaque élément est initialisé en valeur;
  • sinon, l'objet est initialisé à zéro.

Donc, pour résumer

Remarque La citation pertinente de la norme est mise en évidence en en gras

  • nouveau A: initialise par défaut (laisse A :: m non initialisé)
  • new A(): Zero-initialize A, car la valeur initialisée candidate 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é par cv) sans constructeur fourni par l'utilisateur, alors l'objet est initialisé à zéro et, si le constructeur par défaut implicitement déclaré de T est non trivial, ce constructeur est appelé.
  • nouveau B: initialise par défaut (laisse B :: m non initialisé)
  • nouveau B(): valeur initialise B qui initialise tous les champs à zéro; si T est un type de classe (éventuellement qualifié par cv) (Article 9) avec un constructeur fourni par l'utilisateur (12.1), alors le constructeur par défaut pour T est appelé
  • nouveau C: initialise par défaut C, qui appelle le ctor par défaut. si T est un type de classe (éventuellement qualifié par cv) (article 9), le constructeur par défaut de T est appelé, De plus Si le new-initializer est omis, l'objet est initialisé par défaut
  • nouveau C(): valeur initialise C, qui appelle le ctor par défaut. si T est un type de classe (éventuellement qualifié par cv) (Article 9) avec un constructeur fourni par l'utilisateur (12.1), alors le constructeur par défaut pour T est appelé. De plus, Un objet dont l'initialiseur est un ensemble vide de parenthèses, c'est-à-dire (), doit être initialisé en valeur
12
Abhijit