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?
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
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:
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).
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.
- 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.
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.
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.
}
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é.
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.
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
}
En C++ 11, un constructeur par défaut généré par le compilateur est marqué comme supprimé, si:
http://en.cppreference.com/w/cpp/language/default_constructor
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.
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.