web-dev-qa-db-fra.com

Pourquoi le constructeur sans paramètre par défaut disparaît-il lorsque vous en créez un avec des paramètres

En C #, C++ et Java, lorsque vous créez un constructeur en prenant des paramètres, celui sans paramètre par défaut disparaît. Je viens toujours d'accepter ce fait, mais maintenant je me demande pourquoi.

Quelle est la raison de ce comportement? S'agit-il simplement d'une "mesure de sécurité/devinette" disant "Si vous avez créé votre propre constructeur, vous n'êtes probablement pas intéressé par celui-ci implicite"? .__ cela empêche le compilateur d’en ajouter un une fois que vous avez créé vous-même un constructeur?

156
olagjo

Il n'y a aucune raison pour que le compilateur ne puisse pas ajouter le constructeur si vous avez ajouté le vôtre - le compilateur pourrait faire à peu près tout ce qu'il veut! Cependant, vous devez regarder ce qui est le plus logique:

  • Si je n'ai pas défini le constructeur any pour une classe non statique, je souhaite probablement pouvoir instancier cette classe. Afin de permettre cela, le compilateur must ajoutera un constructeur sans paramètre, qui n'aura d'autre effet que d'autoriser l'instanciation. Cela signifie que je n'ai pas besoin d'inclure un constructeur vide dans mon code simplement pour le faire fonctionner.
  • Si j'ai défini mon propre constructeur, en particulier celui avec des paramètres, il est probable que ma propre logique doit être exécutée lors de la création de la classe. Si le compilateur créait un constructeur vide, sans paramètre, dans ce cas, cela permettrait à quelqu'un de sauter la logique que j'avais écrite, ce qui pourrait entraîner la rupture de mon code de toutes sortes de façons. Si je veux un constructeur vide par défaut dans ce cas, je dois le dire explicitement.

Ainsi, dans chaque cas, vous pouvez voir que le comportement des compilateurs actuels est le plus logique en termes de préservation de l'intention probable du code.

218
Dan Puzey

Il n'y a certainement aucune raison technique pour laquelle le langage a soit conçu de cette façon.

Il y a quatre options assez réalistes que je peux voir:

  1. Aucun constructeur par défaut
  2. Le scénario actuel
  3. Toujours fournissant un constructeur par défaut, mais lui permettant d'être explicitement supprimé
  4. Toujours fournir un constructeur par défaut sans lui permettant d'être supprimé

L'option 1 est un peu attrayante, car plus je code, moins je vraiment veux un constructeur sans paramètre. Un jour, je devrais compter combien de fois je (réellement} _ finis par utiliser un constructeur par défaut ...

Option 2 je vais bien avec.

L'option 3 va à contre-courant de Java et de C #, pour le reste du langage. Il n'y a jamais rien que vous "supprimiez" explicitement, sauf si vous comptez explicitement rendre les choses plus privées qu'elles ne le seraient par défaut en Java.

L'option 4 est horrible - vous absolument voulez pouvoir forcer la construction avec certains paramètres. Que signifie même new FileStream()?

Donc, en gros, if vous acceptez le principe selon lequel il est logique de fournir un constructeur par défaut, je pense qu'il est tout à fait logique de le supprimer dès que vous fournissez votre propre constructeur.

68
Jon Skeet

Modifier. En fait, bien que ce que je dis dans ma première réponse soit valable, voici la vraie raison:

Au début, il y avait C. Le C n'est pas orienté objet (vous pouvez adopter une approche OO, mais cela ne vous aide pas et ne fait rien imposer).

Ensuite, il y avait C With Classes, qui a ensuite été renommé C++. C++ est orienté objet, et encourage donc l'encapsulation, et assure un invariant d'objet - lors de la construction et au début et à la fin de toute méthode, l'objet est dans un état valide.

La chose naturelle à faire avec ceci est de faire en sorte qu'une classe ait toujours un constructeur pour s'assurer de démarrer dans un état valide - si le constructeur n'a rien à faire pour garantir cela, alors le constructeur vide documentera ce fait. .

Mais un objectif avec C++ devait être compatible avec C, au point que, dans la mesure du possible, tous les programmes C valides étaient également des programmes C++ valides (l’objectif n’était plus aussi actif, et l’évolution du C séparé en C++ signifie qu’il n’est plus valable. ).

Un des effets en a été la duplication des fonctionnalités entre struct et class. Les premiers font les choses à la manière C (tout le public est public par défaut) et les derniers font les choses bien OO (tout ce qui est privé par défaut, le développeur rend public ce qu'il veut public).

Une autre est que pour qu'une variable C struct, qui ne puisse pas avoir de constructeur parce que C n'a pas de constructeurs, soit valide en C++, il devait alors y avoir un sens à la manière de la regarder en C++. Ainsi, alors que ne pas avoir de constructeur irait à l’encontre de la pratique OO consistant à assurer activement un invariant, C++ a interprété cela comme signifiant qu’il existait un constructeur sans paramètre par défaut qui agissait comme si son corps était vide.

Tous les C structs étaient maintenant valides C++ structs (ce qui signifiait qu'ils étaient identiques à C++ classes avec tout - membres et héritage - public) traités de l'extérieur comme s'il ne possédait qu'un seul constructeur sans paramètre.

Toutefois, si vous avez placé un constructeur dans une variable class ou struct, vous utilisiez la méthode C++/OO plutôt que la méthode C et vous n'avez pas besoin d'un constructeur par défaut.

Comme cela servait de raccourci, les gens continuaient à l'utiliser même quand la compatibilité n'était pas possible autrement (il utilisait d'autres fonctionnalités C++ qui n'étaient pas en C).

Par conséquent, lorsque Java est apparu (basé sur le C++ à bien des égards) et plus tard, C # (basé sur le C++ et Java de différentes manières), ils ont conservé cette approche car les codeurs étaient peut-être déjà habitués.

Stroustrup écrit à ce sujet dans son Le langage de programmation C++ et plus encore, en mettant davantage l’accent sur les "pourquoi" du langage dans La conception et l’évolution du C++ .

=== Réponse originale ===

Disons que cela n'est pas arrivé.

Disons que je ne veux pas d'un constructeur sans paramètre, car je ne peux pas placer ma classe dans un état significatif sans un. En effet, c’est quelque chose qui peut arriver avec struct en C # (mais si vous ne pouvez pas utiliser de manière significative un tout-zéros et des nulls struct en C #, vous utilisez au mieux une optimisation non visible publiquement, et avoir un défaut de conception en utilisant struct).

Pour que ma classe puisse protéger ses invariants, j'ai besoin d'un mot clé removeDefaultConstructor spécial. À tout le moins, il me faudrait créer un constructeur privé sans paramètre pour m'assurer qu'aucun code appelant n'appelle par défaut.

Ce qui complique encore la langue. Mieux vaut ne pas le faire.

Dans l’ensemble, il vaut mieux ne pas penser à ajouter un constructeur comme supprimant le défaut, mais plutôt à ne pas avoir de constructeur du tout comme sucre syntaxique pour ajouter un constructeur sans paramètre qui ne fait rien.

19
Jon Hanna

Le constructeur par défaut sans paramètre est ajouté si vous ne faites rien vous-même pour prendre le contrôle de la création d'objet. Une fois que vous avez créé un seul constructeur pour prendre le contrôle, le compilateur "recule" et vous permet de disposer du contrôle total.

Si ce n'était pas le cas, vous auriez besoin d'un moyen explicite de désactiver le constructeur par défaut si vous voulez que les objets soient constructibles uniquement via un constructeur avec paramètres.

13
Anders Abel

Je pense que la question devrait être l'inverse: Pourquoi n'avez-vous pas besoin de déclarer un constructeur par défaut si vous n'avez défini aucun autre constructeur? 

Un constructeur est obligatoire pour les classes non statiques.
Donc, je pense que si vous n'avez défini aucun constructeur, le constructeur par défaut généré n'est qu'une fonctionnalité pratique du compilateur C #, votre classe ne serait pas valide sans constructeur. Donc, rien à redire à générer implicitement un constructeur qui ne fait rien. Cela semble certainement plus propre que d'avoir des constructeurs vides tout autour. 

Si vous avez déjà défini un constructeur, votre classe est valide, alors pourquoi le compilateur devrait-il supposer que vous voulez un constructeur par défaut? Et si vous n'en voulez pas? Implémenter un attribut pour dire au compilateur de ne pas générer ce constructeur par défaut? Je ne pense pas que ce serait une bonne idée.

3
Botz3000

C'est une fonction pratique du compilateur . Si vous définissez un constructeur avec des paramètres mais ne définissez pas un constructeur sans paramètre, la possibilité que vous ne souhaitiez pas autoriser un constructeur sans paramètre est beaucoup plus grande.

C'est le cas de nombreux objets pour lesquels l'initialisation avec un constructeur vide n'a aucun sens.

Sinon, vous devrez déclarer un constructeur privé sans paramètre pour chaque classe que vous souhaitez restreindre.

À mon avis, ce n'est pas un bon style d'autoriser un constructeur sans paramètre pour une classe qui a besoin de paramètres pour fonctionner.

3
mw_21

Le constructeur par défaut ne peut être construit que lorsque la classe n'a pas de constructeur. Les compilateurs sont écrits de manière à ne fournir cela que comme mécanisme de sauvegarde.

Si vous avez un constructeur paramétré, vous ne voudrez peut-être pas qu'un objet soit créé à l'aide du constructeur par défaut. Si le compilateur avait fourni un constructeur par défaut, vous auriez dû écrire un constructeur no-arg et le rendre privé afin d'empêcher la création d'objets sans argument.

En outre, vous risqueriez davantage d'oublier de désactiver ou de "privatiser" le constructeur par défaut, ce qui entraînerait une erreur fonctionnelle potentielle difficile à détecter.

Et maintenant, vous devez définir explicitement un constructeur no-arg si vous souhaitez qu'un objet soit créé par défaut ou en transmettant des paramètres. Ceci est fortement vérifié et le compilateur se plaint du contraire, assurant ainsi qu'il n'y a pas de faille ici.

1
ashes

Je pense que cela est géré par le compilateur. Si vous ouvrez l'assembly .net dans ILDASM, vous verrez le constructeur par défaut, même s'il ne figure pas dans le code. Si vous définissez un constructeur paramétré, le constructeur par défaut ne sera pas visible. 

En fait, lorsque vous définissez la classe (non statique), le compilateur fournit cette fonctionnalité en pensant que vous allez simplement créer une instance. Et si vous souhaitez effectuer une opération spécifique, vous aurez sûrement votre propre constructeur. 

1
PQubeTechnologies

Local

Ce comportement peut être considéré comme une extension naturelle de la décision pour que les classes aient un constructeur public sans paramètre par défaut. Sur la base de la question qui a été posée, nous prenons cette décision comme prémisse et supposons que nous ne la remettons pas en question dans ce cas.

Méthodes pour supprimer le constructeur par défaut

Il s'ensuit qu'il doit exister un moyen de supprimer le constructeur sans paramètre public par défaut. Cette suppression peut être réalisée des manières suivantes:

  1. Déclarer un constructeur non public sans paramètre
  2. Supprimer automatiquement le constructeur sans paramètre lorsqu'un constructeur avec paramètres est déclaré
  3. Quelques mots-clés/attributs à indiquer au compilateur pour supprimer le constructeur sans paramètre (assez maladroit pour qu’il soit facile d’écarter)

Choisir la meilleure solution

Maintenant nous nous demandons: S'il n'y a pas de constructeur sans paramètre, par quoi faut-il le remplacer? et Dans quels types de scénarios voudrions-nous supprimer le constructeur sans paramètre public par défaut?

Les choses commencent à tomber en place. Premièrement, il doit être remplacé par un constructeur avec des paramètres ou par un constructeur non public. Deuxièmement, les scénarios dans lesquels vous ne souhaitez pas de constructeur sans paramètre sont les suivants:

  1. Nous ne voulons pas du tout que la classe soit instanciée, ou nous voulons contrôler la visibilité du constructeur: déclarer un constructeur non public
  2. Nous voulons forcer la fourniture de paramètres lors de la construction: déclarer un constructeur avec des paramètres

Conclusion

Voilà ce que nous avons - exactement les deux manières par lesquelles C #, C++ et Java autorisent la suppression du constructeur public sans paramètre par défaut.

1
Zaid Masud

C'est parce que lorsque vous ne définissez pas de constructeur, le compilateur génère automatiquement un constructeur qui ne prend aucun argument. Lorsque vous voulez quelque chose de plus d'un constructeur, vous le remplacez. Ce n'est PAS une surcharge de fonction. Le seul constructeur que le compilateur voit maintenant est donc votre constructeur qui prend un argument. Pour contrer ce problème, vous pouvez passer une valeur par défaut si le constructeur est passé sans valeur.

0
Vivek Pradhan

Une classe a besoin d'un constructeur. C'est une exigence obligatoire.

  • Si vous n'en créez pas, le constructeur sans paramètre vous sera automatiquement attribué.
  • Si vous ne voulez pas de constructeur sans paramètre, vous devez créer le vôtre.
  • Si vous avez besoin des deux, constructeur sans paramètre et constructeur basé sur paramètre, vous pouvez les ajouter manuellement.

Je vais répondre à votre question avec un autre, pourquoi voulons-nous toujours un constructeur par défaut sans paramètre? Dans certains cas, cela n’est pas souhaité et le développeur a le contrôle de l’ajouter ou de le supprimer si nécessaire.

0
Jaider