- Le compilateur génère-t-il automatiquement un constructeur par défaut?
- Le constructeur par défaut généré implicitement effectue-t-il une initialisation à zéro?
Si vous analysez de manière légaliste le langage de la norme 2003, alors les réponses sont oui et non. Cependant, ce n'est pas toute l'histoire car contrairement à un constructeur par défaut défini par l'utilisateur, un constructeur par défaut défini implicitement n'est pas toujours utilisé lors de la création d'un objet à partir de zéro -- il existe deux autres scénarios : aucune construction et initialisation des valeurs par membre.
Le cas "aucune construction" est en réalité juste une technicité car il n'est fonctionnellement pas différent que l'appel au constructeur par défaut trivial. L'autre cas est plus intéressant : l'initialisation des valeurs par membre est invoquée en utilisant "()" [comme si on invoquait explicitement un constructeur sans arguments] et cela contourne ce qui est techniquement appelé le constructeur par défaut. À la place, cela effectue de manière récursive une initialisation des valeurs sur chaque membre de données, et pour les types de données primitifs, cela se résout finalement à une initialisation à zéro.
En effet, le compilateur fournit deux différents constructeurs par défaut définis implicitement. Un qui effectue l'initialisation à zéro des données membres primitives et l'autre qui ne le fait pas. Voici quelques exemples de comment vous pouvez invoquer chaque type de constructeur :
MyClass a; // construction par défaut ou aucune construction
MyClass b = MyClass(); // initialisation des valeurs par membre
et
new MyClass; // construction par défaut ou aucune construction
new MyClass(); // initialisation des valeurs par membre
Remarque : Si un constructeur par défaut déclaré par l'utilisateur existe, alors l'initialisation des valeurs par membre appelle simplement cela et s'arrête.
Voici une analyse quelque peu détaillée de ce que dit la norme à ce sujet...
-
Si vous ne déclarez pas de constructeur, le compilateur crée implicitement un constructeur par défaut [12.1-5]
-
Le constructeur par défaut n'initialise pas les types primitifs [12.1-7]
MyClass() {} // constructeur implicitement défini
-
Si vous initialisez un objet avec "()", cela n'invoque pas directement le constructeur par défaut. À la place, cela déclenche une longue séquence de règles appelée initialisation des valeurs [8.5-7]
-
L'effet net de l'initialisation des valeurs est que le constructeur par défaut déclaré implicitement n'est jamais appelé. À la place, une initialisation des valeurs par membre récursive est invoquée qui initialise à zéro tout membre primitif et appelle le constructeur par défaut sur tout membre qui a un constructeur déclaré par l'utilisateur [8.5-5]
-
L'initialisation des valeurs s'applique même aux types primitifs -- ils seront initialisées à zéro. [8.5-5]
int a = int(); // équivalent à int a = 0;
Tout ceci est en réalité peu significatif pour la plupart des cas. L'auteur d'une classe ne peut généralement pas supposer que les membres de données seront remis à zéro lors d'une séquence d'initialisation implicite -- donc toute classe auto-gérée devrait définir son propre constructeur si elle a des membres de données primitifs qui nécessitent une initialisation.
Alors quand est-ce que cela importe?
-
Il peut y avoir des circonstances où du code générique veut forcer l'initialisation de types inconnus. L'initialisation des valeurs fournit un moyen de le faire. Souvenez-vous simplement que l'initialisation implicite à zéro ne se produit pas si l'utilisateur a fourni un constructeur.
-
Par défaut, les données contenues par std::vector sont initialisées par valeur. Cela peut empêcher les débogueurs de mémoire d'identifier les erreurs logiques associées à des tampons mémoire autrement non initialisés.
vector::resize( size_type sz, T c=T() ); // c par défaut est "initié par valeur"
-
Des tableaux entiers de types primitifs ou de structures de type "plain-old-data" (POD) peuvent être initialisés à zéro en utilisant une syntaxe d'initialisation des valeurs.
new int[100]();
Ce post a plus de détails sur les variations entre les versions de la norme, et il note également un cas où la norme est appliquée différemment dans les principaux compilateurs.
36 votes
On dirait que vous avez besoin d'un meilleur livre. ;)
13 votes
Oui. Erreur impardonnable, si les "zéros chaque membre de données" est effectivement une citation littérale.
3 votes
Je pense que c'est une idée fausse commune en langage c. J'ai pensé la même chose jusqu'à ce que je visite cette page. Je suis sûr de l'avoir lu quelque part ou appris en cours.
0 votes
Je soupçonne que cette confusion vient lorsque les gens apprennent que les objets globaux sont garantis d'être mis à zéro, bien qu'il soit vraiment préférable de ne pas compter uniquement là-dessus.