Quelles sont toutes les fonctions membres créées par le compilateur pour une classe? Cela se produit-il tout le temps? comme destructeur. Ma préoccupation est de savoir si elle est créée pour toutes les classes, et pourquoi le constructeur par défaut est-il nécessaire?
S'ils sont nécessaires,
Comme l'a dit Péter dans un commentaire utile, tous ceux-ci ne sont générés que par le compilateur quand ils sont nécessaires. (La différence est que, lorsque le compilateur ne peut pas les créer, c'est Ok tant qu'ils ne sont pas utilisés.)
C++ 11 ajoute les règles suivantes, qui sont également valables pour C++ 14 (crédits à towi, voir ce commentaire ):
Notez que ces règles sont un peu plus élaborées que les règles C++ 03 et ont plus de sens dans la pratique.
Pour une compréhension plus facile de ce qui est décrit ci-dessus, les éléments pour Thing
:
class Thing {
public:
Thing(); // default constructor
Thing(const Thing&); // copy c'tor
Thing& operator=(const Thing&); // copy-assign
~Thing(); // d'tor
// C++11:
Thing(Thing&&); // move c'tor
Thing& operator=(Thing&&); // move-assign
};
Et comme lecture supplémentaire, si vous êtes un débutant en C++ - envisagez une conception qui ne vous oblige à implémenter aucun des cinq derniers, aka The Rule Of Zero (par Martinho Fernandes ).
Voulez-vous dire "défini" par "créé"?
$ 12.1 - "Le constructeur par défaut (12.1), le constructeur de copie et l'opérateur d'affectation de copie (12.8) et le destructeur (12.4) sont des fonctions membres spéciales.
Si "créé" signifie "défini", voici les parties importantes de la norme C++.
-Un constructeur par défaut déclaré implicitement pour une classe est implicitement défini lorsqu'il est utilisé pour créer un objet de son type de classe (1.8).
-Si une classe n'a pas de destructeur déclaré par l'utilisateur, un destructeur est déclaré implicitement. Un destructeur déclaré implicitement est défini implicitement lorsqu'il est utilisé pour détruire un objet de son type de classe.
-Si la définition de classe ne déclare pas explicitement un constructeur de copie, l'un est déclaré implicitement. Un constructeur de copie déclaré implicitement est défini implicitement s'il est utilisé pour initialiser un objet de son type de classe à partir d'une copie d'un objet de son type de classe ou d'un type de classe dérivé de son type de classe).
-Si la définition de classe ne déclare pas explicitement un opérateur d'affectation de copie, l'un est déclaré implicitement. Un opérateur d'affectation de copie déclaré implicitement est défini implicitement lorsqu'un objet de son type de classe se voit attribuer une valeur de son type de classe ou une valeur d'un type de classe dérivée de son type de classe.
Par défaut, s'il n'est pas implémenté par l'utilisateur, le compilateur ajoute des fonctions membres à la classe. On les appelle les quatre grands:
En fonction des types de membres et de la fonction membre répertoriée que vous vous fournissez, ceux-ci ne seront pas tous générés.
D'autres réponses vous ont dit ce qui a été créé et que le compilateur ne peut les générer que s'il est utilisé.
Ma préoccupation est de savoir si elle est créée pour toutes les classes ...
Pourquoi inquiet? Vous pensez que cela crée du code indésirable dans l'exécutable? Peu probable, mais vous pouvez vérifier assez facilement avec votre environnement.
Ou peut-être votre souci était-il que cela ne crée pas un constructeur quand vous en voulez un? Rien à craindre ... ils sont toujours créés si nécessaire et non fournis par l'utilisateur.
... et pourquoi le constructeur par défaut est-il nécessaire?
Parce que les classes peuvent contenir des objets avec leurs propres destructeurs qui doivent être systématiquement invoqués. Par exemple, étant donné ...
struct X
{
std::string a;
std::string b;
};
... le destructeur par défaut s'assure que les destructeurs pour a et b fonctionnent.
Projet standard C++ 17 N4659
https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1 "Déclarations et définitions" contient une note qui les résume probablement toutes:
3 [Remarque: dans certaines circonstances, les implémentations C++ définissent implicitement le constructeur par défaut (15.1), le constructeur de copie (15.8), le constructeur de déplacement (15.8), l'opérateur d'affectation de copie (15.8), l'opérateur d'affectation de déplacement (15.8) ou le destructeur ( 15.4) fonctions des membres. - note de fin] [Exemple: Étant donné
#include <string> struct C { std::string s; // std::string is the standard library class (Clause 24) }; int main() { C a; C b = a; b = a; }
l'implémentation définira implicitement des fonctions pour rendre la définition de C équivalente à
struct C { std::string s; C() : s() { } C(const C& x): s(x.s) { } C(C&& x): s(static_cast<std::string&&>(x.s)) { } // : s(std::move(x.s)) { } C& operator=(const C& x) { s = x.s; return *this; } C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; } // { s = std::move(x.s); return *this; } ~ C() { } };
- fin exemple]
Les conditions dans lesquelles celles-ci sont déclarées sont expliquées à: Conditions pour la génération automatique du ctor par défaut/copier/déplacer et de l'opérateur d'affectation copier/déplacer?
Une bonne façon de s'assurer que quelque chose a un défaut est d'essayer de le faire utiliser = default
comme expliqué à: Que signifie "par défaut" après la déclaration d'une fonction de classe?
L'exemple ci-dessous fait cela, et exerce également toutes les fonctions définies implicitement.
#include <cassert>
#include <string>
struct Default {
int i;
Default() = default;
Default(const Default&) = default;
Default& operator=(Default&) = default;
Default& operator=(const Default&) = default;
Default(Default&&) = default;
Default& operator=(Default&&) = default;
~Default() = default;
};
struct Instrument {
int i;
static std::string last_call;
Instrument() { last_call = "ctor"; }
Instrument(const Instrument&) { last_call = "copy ctor"; }
Instrument& operator=(Instrument&) { last_call = "copy assign"; return *this; }
Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
Instrument(Instrument&&) { last_call = "move ctor"; }
Instrument& operator=(Instrument&&) { last_call = "move assign"; return *this; }
~Instrument() { last_call = "dtor"; }
};
std::string Instrument::last_call;
int main() {
// See what the default constructors are doing.
{
// Default constructor.
Default ctor;
// i is uninitialized.
// std::cout << ctor.i << std::endl;
ctor.i = 1;
// Copy constructor.
Default copy_ctor(ctor);
assert(copy_ctor.i = 1);
// Copy assignment.
Default copy_assign;
copy_assign = ctor;
assert(copy_assign.i = 1);
// Copy assignment const.
const Default const_ctor(ctor);
Default copy_assign_const;
copy_assign_const = const_ctor;
assert(copy_assign_const.i == 1);
// Move constructor.
Default move_ctor(std::move(ctor));
assert(move_ctor.i == 1);
// Move assignment.
Default move_assign;
move_assign = std::move(ctor);
assert(move_assign.i == 1);
}
// Check that the constructors are called by these calls.
{
// Default constructor.
Instrument ctor;
assert(Instrument::last_call == "ctor");
// Copy constructor.
Instrument copy_ctor(ctor);
assert(Instrument::last_call == "copy ctor");
// Copy assignment.
copy_ctor = ctor;
assert(Instrument::last_call == "copy assign");
// Copy assignment const.
const Instrument const_ctor(ctor);
Instrument copy_assign_const;
copy_assign_const = const_ctor;
assert(Instrument::last_call == "copy assign const");
// Move constructor.
Instrument move_ctor(std::move(ctor));
assert(Instrument::last_call == "move ctor");
// Move assignment.
Instrument move_assign;
move_assign = std::move(ctor);
assert(Instrument::last_call == "move assign");
// Destructor.
{
Instrument dtor;
}
assert(Instrument::last_call == "dtor");
}
}
Testé avec GCC 7.3.0:
g++ -std=c++11 implicitly_defined.cpp