web-dev-qa-db-fra.com

Existe-t-il un constructeur implicite par défaut en C ++?

Dans le livre que je lis en ce moment ( C++ Without Fear ), il est dit que si vous ne déclarez pas de constructeur par défaut pour une classe, le compilateur vous en fournit un, qui "met à zéro chaque membre de données ". J'ai expérimenté cela et je ne vois aucun comportement de mise à zéro. Je ne trouve pas non plus quoi que ce soit qui le mentionne sur Google. Est-ce juste une erreur ou une bizarrerie d'un compilateur spécifique?

62
Skilldrick

Si vous ne définissez pas de constructeur, le compilateur définira pour vous un constructeur par défaut.

La mise en œuvre de cette

le constructeur par défaut est:

  • construction par défaut de la classe de base (si la classe de base n'a pas de constructeur par défaut, il s'agit d'un échec de compilation)
  • construire par défaut chaque variable membre dans l'ordre de déclaration. (Si un membre n'a pas de constructeur par défaut, il s'agit d'un échec de compilation).

Remarque:
Les données POD (int, float, pointer, etc.) n'ont pas de constructeur explicite mais l'action par défaut est de ne rien faire (dans la palette de la philosophie C++; nous ne voulons pas payer pour quelque chose à moins que nous le demander explicitement).

Si aucun opérateur destructeur/copieur constructeur/affectation n'est défini, le compilateur en crée un pour vous (donc une classe a toujours un destructeur/copieur constructeur/opérateur d'assignation (sauf si vous trichez et déclarez explicitement un mais ne le définissez pas)).
L'implémentation par défaut est:

Destructeur:

  • Si le destructeur défini par l'utilisateur est défini, exécutez le code fourni.
  • Appelez le destructeur de chaque membre dans l'ordre inverse de la déclaration
  • Appelez le destructeur de la classe de base.

Constructeur de copie:

  • Appelez le constructeur de copie de la classe Base.
  • Appelez le constructeur de copie pour chaque variable membre dans l'ordre de déclaration.

Opérateur d'assignation:

  • Appelez l'opérateur d'affectation de classe de base
  • Appelez l'opérateur d'affectation de chaque variable membre dans l'ordre de déclaration.
  • Renvoyez une référence à cela.

Remarque L'opérateur de construction/affectation de copie des données POD copie simplement les données (d'où le problème de copie superficielle associé aux pointeurs RAW).

63
Martin York

Je pense qu'il vaut la peine de souligner que le constructeur par défaut ne sera créé par le compilateur que si vous fournissez pas de constructeur du tout. Cela signifie que si vous ne fournissez qu'un seul constructeur qui prend un argument, le compilateur pas créera le constructeur sans argument par défaut pour vous.

Le comportement de mise à zéro dont parle votre livre est probablement spécifique à un compilateur particulier. J'ai toujours supposé que cela peut varier et que vous devez initialiser explicitement tous les membres de données.

37
Bill the Lizard
  • Le compilateur génère-t-il automatiquement un constructeur par défaut?
  • Le constructeur par défaut généré implicitement n'effectue-t-il aucune initialisation?

Si vous analysez légalement le langage de la norme 2003, les réponses sont oui , et non . Cependant, ce n'est pas toute l'histoire parce que 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: pas de construction et initialisation de la valeur par membre.

Le cas "pas de construction" est vraiment juste une technicité car il n'est fonctionnellement pas différent de l'appel du constructeur par défaut trivial. L'autre cas est plus intéressant: l'initialisation de la valeur au niveau des membres est invoquée en utilisant "()" [comme s'il appelait explicitement un constructeur qui n'a pas d'arguments] et il contourne ce qui est techniquement appelé as the constructeur par défaut . Au lieu de cela, il effectue récursivement l'initialisation de la valeur sur chaque membre de données, et pour les types de données primitifs, cela se résout finalement en initialisation zéro .

Donc, en effet, le compilateur fournit deux constructeurs par défaut définis implicitement . L'un d'eux le fait n'effectue aucune initialisation des données des membres primitifs et l'autre non. Voici quelques exemples de la façon dont vous pouvez appeler chaque type de constructeur:

    MyClass a; // default-construction or no construction
    MyClass b = MyClass(); // member-wise value-initialization

et

    new MyClass; // default-construction or no construction
    new MyClass(); // member-wise value-initialization

Remarque: Si un constructeur par défaut par défaut existe existe, alors l'initialisation de la valeur au niveau des membres appelle simplement cela et s'arrête.


Voici une ventilation assez détaillée de ce que la norme dit à 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() {} // implicitly defined constructor
    
  • Si vous initialisez un objet avec "()", cela n'invoque pas directement le constructeur par défaut. Au lieu de cela, il déclenche une longue séquence de règles appelée initialisation de valeur [8.5-7]

  • L'effet net de l'initialisation de la valeur est que le constructeur par défaut déclaré implicitement n'est jamais appelé . Au lieu de cela, une initialisation récursive de la valeur au niveau des membres est invoquée qui, finalement initialisera à zéro tous les membres primitifs et appellera le constructeur par défaut sur tous les membres ayant un utilisateur -déclaré constructeur [8.5-5]

  • L'initialisation de la valeur s'applique même aux types primitifs - ils seront initialisés à zéro. [8.5-5]

    a = int(); // equivalent to int a=0;
    

Tout cela est vraiment théorique pour la plupart des usages. Le rédacteur d'une classe ne peut généralement pas supposer que les membres de données seront mis à zéro pendant une séquence d'initialisation implicite - donc toute classe autogé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 important?

  • Il peut y avoir des circonstances où le code générique veut forcer l'initialisation de types inconnus. L'initialisation de la valeur fournit un moyen de le faire. N'oubliez pas 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 en valeur. Cela peut empêcher les débogueurs de mémoire d'identifier les erreurs logiques associées à des tampons de mémoire non initialisés.

    vector::resize( size_type sz, T c=T() ); // default c is "value-initialized"
    
  • Des tableaux entiers de type primitif ou de structures de type "plain-old-data" (POD) peuvent être initialisés à zéro en utilisant la syntaxe d'initialisation de valeur.

    new int[100]();
    

Cet article 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.

34
nobar

C++ génère un constructeur par défaut, mais uniquement si vous n'en fournissez pas un. La norme ne dit rien sur la mise à zéro des membres de données. Par défaut, lorsque vous construisez un objet pour la première fois, ils ne sont pas définis.

Cela peut être déroutant car la plupart des types primitifs C++ ont des "constructeurs" par défaut qui les initialisent à zéro (int (), bool (), double (), long (), etc. ), mais le compilateur ne les appelle pas pour initier les membres POD comme il le fait pour les membres objet.

Il convient de noter que la STL utilise ces constructeurs pour construire par défaut le contenu des conteneurs qui contiennent des types primitifs. Vous pouvez jeter un œil à cette question pour plus de détails sur la façon dont les choses dans les conteneurs STL sont initiées.

20
Todd Gamblin

Le constructeur par défaut créé pour une classe n'initialisera pas les types intégrés, mais il appellera le constructeur par défaut sur tous les membres définis par l'utilisateur:

class Foo
{
public:
     int x;
     Foo() : x(1) {}
};

class Bar
{
public:
     int y;
     Foo f;
     Foo *fp;
};

int main()
{

    Bar b1; 
    ASSERT(b1.f.x == 1); 
    // We know nothing about what b1.y is set to, or what b1.fp is set to.

    // The class members' initialization parallels normal stack initialization.
    int y;  
    Foo f; 
    Foo *fp; 
    ASSERT(f.x == 1);
    // We know nothing about what y is set to, or what fp is set to.

}
11
Eclipse

Le compilateur générera des constructeurs et destructeurs par défaut si ceux créés par l'utilisateur ne sont pas présents. Ceux-ci ne modifieront PAS l'état des membres de données.

En C++ (et C), le contenu des données allouées n'est pas garanti. Dans les configurations de débogage, certaines plates-formes définiront cela à une valeur connue (par exemple 0xFEFEFEFE) pour aider à identifier les bogues, mais cela ne devrait pas être invoqué.

5
Andrew Grant

C++ ne pas garantit la remise à zéro de la mémoire. Java et C # do (en quelque sorte).

Certains compilateurs le pourraient, mais n'en dépendent pas.

4
Jason Cohen

La remise à zéro ne se produit que pour les globaux. Donc, si votre objet est déclaré dans la portée globale, ses membres seront mis à zéro:

class Blah
{
public:
    int x;
    int y;
};

Blah global;

int main(int argc, char **argv) {
    Blah local;
    cout<<global.x<<endl;  // will be 0
    cout<<local.x<<endl;   // will be random
}
4
codelogic

En C++ 11, un constructeur par défaut généré par le compilateur est marqué comme supprimé, si:

  • la classe a un champ de référence
  • ou un champ const sans constructeur par défaut défini par l'utilisateur
  • ou un champ sans initialiseur par défaut, avec un constructeur par défaut supprimé

http://en.cppreference.com/w/cpp/language/default_constructor

2
Nice Books

C++ génère un constructeur par défaut. Si nécessaire (déterminé au moment de la compilation, je crois), il générera également un constructeur de copie par défaut et un constructeur d'affectation par défaut. Je n'ai cependant rien entendu sur les garanties de remise à zéro de la mémoire.

1
Jason Punyon

Le compilateur par défaut ne générera pas le constructeur par défaut, sauf si l'implémentation n'en requiert pas. Donc, fondamentalement, le constructeur doit être un constructeur non trivial.

Pour que le constructeur soit un constructeur non trivial, voici les conditions dans lesquelles chacun peut suffire:

1) La classe a une fonction membre virtuelle. 2) Les sous-objets membres de classe ou les classes de base ont des constructeurs non triviaux. 3) Une classe a une hiérarchie d'héritage virtuelle.

1
rahul