Je prépare mon examen CPP et l'une des questions est la suivante: pouvez-vous supprimer le constructeur de classe par défaut et, dans l'affirmative, quelle serait la raison de le faire? OK, vous pouvez évidemment le faire:
class MyClass
{
public:
MyClass() = delete;
};
mais je ne comprends pas pourquoi voudriez-vous le faire?
Considérez la classe suivante:
struct Foo {
int i;
};
Cette classe est un agrégat et vous pouvez créer une instance avec les trois définitions suivantes:
int main() {
Foo f1; // i uninitialized
Foo f2{}; // i initialized to zero
Foo f3{42}; // i initialized to 42
}
Supposons maintenant que vous n'aimiez pas les valeurs non initialisées et le comportement non défini qu'elles pourraient générer. Vous pouvez supprimer le constructeur par défaut de Foo
:
struct Foo {
Foo() = delete;
int i;
};
Foo
est toujours un agrégat, mais seules les deux dernières définitions sont valides - la première est maintenant une erreur de compilation.
Il y a quelques raisons de supprimer le constructeur par défaut.
= delete
est un problème de style, comme @HolyBlackCat l'a dit, mais cela clarifie votre intention en indiquant le code client à niquement appelez le constructeur avec les paramètres.Si la deuxième déclaration n'était pas claire, considérons l'exemple suivant:
class A
{
public:
//A() = delete;
A(int, int) {};
};
Si vous essayez maintenant d'appeler le constructeur par défaut, vous obtiendrez une erreur ressemblant à quelque chose comme (GCC 7.2):
erreur: pas de fonction correspondante pour l'appel à 'A :: A ()'
Cependant, si vous décommentez la ligne avec le = delete
, alors vous obtenez ce qui suit:
erreur: utilisation de la fonction supprimée 'A :: A ()'
Cela montre clairement que l’on essaie d’utiliser un constructeur supprimé par rapport à l’autre erreur, ce qui est quelque peu flou.
choix par défaut multiples
La suppression du constructeur par défaut d'une classe est une bonne idée lorsqu'il existe plusieurs choix pour l'état par défaut ou non initialisé. Par exemple, supposons que j'ai une classe,
template<typename F>
class Polynomial;
qui représente un polynôme sur un champ, F
. Dans ce cas, il existe de nombreux choix pour une valeur par défaut du polynôme. L’un pourrait être l’identité pour les polynômes en addition, c’est-à-dire zéro, mais nous pourrions aussi avoir l’identité multiplicative, l’unité. Cela dépend de la manière dont l'utilisateur a l'intention de raisonner sur la classe et de son utilisation.
Pas de choix par défaut
L'autre cas notable est celui où, au lieu d'avoir plusieurs états par défaut qui pourraient avoir un sens, nous n'en avons aucun. Par exemple, supposons que nous ayons une classe représentant un manifold ou une surface.
Qu'est-ce que c'est alors Manifold()
? C'est un espace vide, sans rien, aucune surface, aucune notion de distance ou de métrique. Mais alors cela n’a aucun sens de penser à cela comme une variété, mais plutôt peut-être quelque chose de plus général comme un espace topologique.
Donc, dans ce cas, je choisirais également de supprimer le constructeur par défaut.
Parfois, les classes ne sont pas destinées à être instanciées.
Un exemple qui n’a pas encore été mentionné est celui des classes de "traits". Par exemple, considérons std::char_traits
, bien que la norme n’ait pas dit que son constructeur par défaut devait être supprimé, son constructeur par défaut n’était guère utile car la classe elle-même était vide, toutes ses fonctions étaient statiques et il n’était pas nécessaire de les instancier. d'utiliser les alias de type qu'il fournit.
Les classes de caractères ne sont généralement utilisées que pour supporter d’autres classes (généralement d’autres classes de modèles). Il est donc logique de supprimer leur constructeur par défaut - pour renforcer l’idée qu’elles ne sont qu’un type d’assistance et qu’elles ne sont pas censées être utilisées de la sorte. un objet.