Que signifient les expressions suivantes en C ++: initialisation à zéro, par défaut et par valeur?

190

Que signifient les phrases suivantes en C ++:

  • zéro-initialisation,

  • initialisation par défaut, et

  • initialisation de la valeur

Que doit savoir un développeur C ++ à leur sujet?

Facture
la source
1
Ceci est lié (mais pas identique à) stackoverflow.com/questions/620137/…
Steve Jessop
20
Il y a plus! La liste complète des initialisations: valeur, direct, copie, liste (nouvelle intro C ++ 11), agrégat, référence, zéro, constante et par défaut; fr.cppreference.com/w/cpp/language/initialization les répertorie tous avec des exemples :)
legends2k

Réponses:

65

Une chose à réaliser est que «l'initialisation de la valeur» est nouvelle avec la norme C ++ 2003 - elle n'existe pas dans la norme originale de 1998 (je pense que c'est peut-être la seule différence qui est plus qu'une clarification). Voir la réponse de Kirill V. Lyadvinsky pour les définitions directement issues de la norme.

Voir cette réponse précédente sur le comportement de operator newpour plus de détails sur les différents comportements de ces types d'initialisation et quand ils se déclenchent (et quand ils diffèrent de C ++ 98 à C ++ 03):

Le point principal de la réponse est:

Parfois, la mémoire retournée par l'opérateur new sera initialisée, et parfois cela ne dépendra pas si le type que vous créez est un POD, 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 par défaut
  • Dans C ++ 2003, un troisième type d'initialisation, l'initialisation de la valeur a été ajoutée.

Pour dire le moins, c'est plutôt complexe et quand les différentes méthodes entrent en jeu sont subtiles.

Il faut certainement savoir que MSVC suit les règles du C ++ 98, même dans VS 2008 (VC 9 ou cl.exe version 15.x).

L'extrait suivant montre que MSVC et Digital Mars suivent les règles C ++ 98, tandis que GCC 3.4.5 et Comeau suivent les règles C ++ 03:

#include <cstdio>
#include <cstring>
#include <new>

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

int main()
{
    char buf[sizeof(B)];
    std::memset( buf, 0x5a, sizeof( buf));

    // use placement new on the memset'ed buffer to make sure 
    //  if we see a zero result it's due to an explicit 
    //  value initialization
    B* pB = new(buf) B();   //C++98 rules - pB->m is uninitialized
                            //C++03 rules - pB->m is set to 0
    std::printf( "m  is %d\n", pB->m);
    return 0;
}
Michael Burr
la source
1
Pas que cela compte int, mais m()sur la troisième ligne, la valeur initialise m. Important si vous passez int m;à B m;. :)
Johannes Schaub - litb
Droite - Aet Cne sont pas utilisés dans cet exemple (ils sont reportés de l'autre réponse liée). Même si C ++ 98 et C ++ 03 utilisent une terminologie différente pour décrire comment Aet Csont construits, le résultat est le même dans les deux normes. Il en struct Brésulte seulement un comportement différent.
Michael Burr
1
ce que je voulais dire, c'est que si vous changez C en struct C { C() : m() {}; ~C(); B m; };, alors vous aurez m.mla valeur 0. Mais s'il était initialisé par défaut mcomme vous le dites C ++ 03, alors il m.mne serait pas initialisé comme dans C ++ 98.
Johannes Schaub - litb le
1
Commentaires supplémentaires intéressants sur la gestion MSVC de cette fonctionnalité: stackoverflow.com/questions/3931312/…
Brent Bradburn
Quelle initialisation a lieu lorsque vous déclarez votre type comme une variable locale, c'est-à-dire au niveau de la pile?
André Puel
89

Norme C ++ 03 8.5 / 5:

Initialiser à zéro un objet de type T signifie:
- si T est un type scalaire (3.9), l'objet est mis à la valeur 0 (zéro) convertie en T;
- si T est un type de classe non-union, chaque membre de données non statique et chaque sous-objet de classe de base est initialisé à zéro;
- si T est un type union, le premier membre de données nommé de l'objet est initialisé à zéro;
- si T est un type tableau, chaque élément est initialisé à zéro;
- si T est un type référence, aucune initialisation n'est effectuée.

Pour default-initialiser un objet de moyens de type T:
- si T est un type de classe non-POD (clause 9), le constructeur par défaut pour T est appelé (et l'initialisation est mal formé 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, l'objet est initialisé à zéro.

Pour la valeur initialiser un objet de moyens de type T:
- si T est un type de classe (clause 9) avec un constructeur déclarée par l' utilisateur (12.1), le constructeur par défaut pour T est appelé (et l'initialisation est mal formé si T n'a pas de constructeur par défaut accessible);
- si T est un type de classe non-union sans constructeur déclaré par l'utilisateur, alors chaque membre de données non statique et composant de classe de base de T est initialisé par la valeur;
- si T est un type tableau, alors chaque élément est initialisé par une valeur;
- sinon, l'objet est initialisé à zéro

Un programme qui appelle une initialisation par défaut ou une initialisation de valeur d'une entité de type référence est mal formé. Si T est un type qualifié cv, la version non qualifiée cv de T est utilisée pour ces définitions d'initialisation zéro, d'initialisation par défaut et d'initialisation de valeur.

Kirill V. Lyadvinsky
la source
18
Cela peut être obsolète pour C ++ 11. cppreference.com indique que l'initialisation par défaut n'initialise pas les membres à zéro (seule l'initialisation de la valeur le fait).
Alexei Sholik
3
@android soulève un point important, auquel je ne vois pas de réponse ailleurs, j'ai donc posé une nouvelle question. stackoverflow.com/questions/22233148/…
Adrian McCarthy