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.
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éronew 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 )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) pourT
est appelé (et l'initialisation est incorrecte siT
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 pourT
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, siT
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.
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
C++ 11 (en référence à n3242)
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}
{}
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.
Effectué dans les cas suivants
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.
Réalisé dans les cas suivants
Initialiser par défaut un objet de type T signifie:
- si T est un (éventuellement qualifié cv)
non PODtype 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é.
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