¿Por qué desaparece el constructor sin parámetros predeterminado cuando crea uno con parámetros?


161

En C #, C ++ y Java, cuando crea un constructor que toma parámetros, el sin parámetros predeterminado desaparece. Siempre he aceptado este hecho, pero ahora he empezado a preguntarme por qué.

¿Cuál es la razón de este comportamiento? ¿Es solo una "medida de seguridad / adivinanza" que dice "Si ha creado un constructor propio, probablemente no quiera que este implícito esté dando vueltas"? ¿O tiene una razón técnica que hace imposible que el compilador agregue uno una vez que haya creado un constructor usted mismo?


77
Puede agregar C ++ a la lista de lenguajes que tienen este comportamiento.
Henk Holterman

18
Y en C ++ ahora puede decir Foo() = default;que recupere el predeterminado.
MSalters

1
Si su constructor con parámetros puede tener argumentos predeterminados para todos sus parámetros, entraría en conflicto con el constructor sin parámetros incorporado, de ahí la necesidad de eliminarlo cuando cree el suyo propio.
Morwenn

3
Imagínese estar presente para este debate entre los fundadores del primer compilador que hizo el requisito predeterminado del constructor y la acalorada discusión que habría inspirado.
kingdango

77
@HenkHolterman C ++ no es solo otro con este comportamiento, sino el autor, y es para permitir la compatibilidad con C, como lo discutió Stroustrup en The Design and Evolution of C ++ y como se resume en mi respuesta actualizada.
Jon Hanna

Respuestas:


219

No hay razón para que el compilador no pueda agregar el constructor si ha agregado el suyo, ¡el compilador podría hacer casi lo que quiera! Sin embargo, debes mirar lo que tiene más sentido:

  • Si no he definido ningún constructor para una clase no estática, lo más probable es que quiera poder crear una instancia de esa clase. Para permitir eso, el compilador debe agregar un constructor sin parámetros, que no tendrá ningún efecto sino permitir la creación de instancias. Esto significa que no tengo que incluir un constructor vacío en mi código solo para que funcione.
  • Si he definido un constructor propio, especialmente uno con parámetros, lo más probable es que tenga una lógica propia que deba ejecutarse al crear la clase. Si el compilador creara un constructor vacío y sin parámetros en este caso, permitiría a alguien omitir la lógica que había escrito, lo que podría llevar a que mi código se rompa de muchas maneras. Si quiero un constructor vacío predeterminado en este caso, necesito decirlo explícitamente.

Entonces, en cada caso, puede ver que el comportamiento de los compiladores actuales tiene más sentido en términos de preservar la intención probable del código.


2
Creo que el resto de su respuesta prueba que su primera oración es incorrecta.
Konrad Rudolph el

76
@KonradRudolph, la primera oración dice que un compilador podría agregar un constructor en este escenario; el resto de la respuesta explica por qué no lo hace (para los idiomas especificados en la pregunta)
JohnL

1
Bueno no. Si diseñamos un lenguaje OO desde cero, el significado más obvio de que no hay un constructor es: "no se agregó un constructor que garantice que la clase sea" invariante "y generaría un error de compilación.
Jon Hanna

70

Ciertamente no hay una razón técnica por la cual el lenguaje tiene que ser diseñado de esta manera.

Hay cuatro opciones algo realistas que puedo ver:

  1. No hay constructores predeterminados en absoluto
  2. El escenario actual
  3. Siempre proporciona un constructor predeterminado por defecto, pero permite que se suprima explícitamente
  4. Proporcionar siempre un constructor predeterminado sin permitir que se suprima

La opción 1 es algo atractiva, ya que cuanto más codifico, con menos frecuencia realmente quiero un constructor sin parámetros. Algún día debería contar la frecuencia con la que realmente termino usando un constructor predeterminado ...

Opción 2 con la que estoy bien.

La opción 3 va en contra del flujo de Java y C #, para el resto del lenguaje. Nunca hay nada que "elimine" explícitamente, a menos que cuente explícitamente hacer las cosas más privadas de lo que serían por defecto en Java.

La opción 4 es horrible: absolutamente desea poder forzar la construcción con ciertos parámetros. ¿Qué new FileStream()significaría incluso?

Básicamente, si acepta la premisa de que proporcionar un constructor predeterminado tiene sentido, creo que tiene mucho sentido suprimirlo tan pronto como proporcione su propio constructor.


1
Me gusta la opción 3, porque cuando escribo algo, necesito más a menudo tener ambos tipos de constructores y luego solo constructor con parámetro. Entonces preferiría que un constructor sin parámetros sea privado y luego escribir 10 veces al día constructores sin parámetros. Pero eso es probablemente solo yo, estoy escribiendo muchas clases serias ...
Petr Mensik

8
@PetrMensik: Si su constructor sin parámetros realmente no necesita hacer absolutamente nada, es una línea que seguramente no requeriría mucho más código que una declaración de "eliminación explícita".
Jon Skeet

2
@PetrMensik: Lo siento, mi error, sí. Eso es definitivamente lo opuesto a mi experiencia, y diría que incluir algo automáticamente también es la opción más peligrosa ... si accidentalmente terminas sin excluirlo, puedes arruinar a tus invariantes, etc.
Jon Skeet

2
# 4 es el caso con C # structs, para bien o para mal.
Jay Bazuzi

44
Me gusta la opción 1 porque es más fácil de entender. No hay un constructor "mágico" que no puedas ver en el código. Con la opción 1, el compilador debe emitir una advertencia cuando (¿o tal vez incluso no permitir?) Una clase no estática no tiene constructores de instancias. ¿Qué tal: "No se encontraron constructores de instancias para la clase <TYPE>. ¿Querías declarar la clase estática?"
Jeppe Stig Nielsen

19

Editar. En realidad, aunque lo que digo en mi primera respuesta es válido, esta es la verdadera razón:

Al principio existía C. C no está orientado a objetos (puede adoptar un enfoque OO, pero no lo ayuda ni impone nada).

Luego estaba C With Classes, que luego pasó a llamarse C ++. C ++ está orientado a objetos y, por lo tanto, fomenta la encapsulación y garantiza la invariabilidad de un objeto: después de la construcción y al principio y al final de cualquier método, el objeto está en un estado válido.

Lo natural que se debe hacer con esto es hacer cumplir que una clase siempre debe tener un constructor para garantizar que comience en un estado válido; si el constructor no tiene que hacer nada para garantizarlo, el constructor vacío documentará este hecho. .

Pero un objetivo con C ++ era ser compatible con C hasta el punto de que, en la medida de lo posible, todos los programas válidos de C también eran programas válidos de C ++ (ya no es un objetivo tan activo, y la evolución de C separada de C ++ significa que ya no se cumple )

Un efecto de esto fue la duplicación en la funcionalidad entre structy class. El primero hace las cosas a la manera C (todo público por defecto) y el segundo hace las cosas de una buena manera OO (todo lo privado por defecto, el desarrollador hace público lo que quiere público).

Otra es que para que una C struct, que no podría tener un constructor porque C no tiene constructores, sea válida en C ++, tenía que haber un significado para esto en la forma de verla en C ++. Y así, aunque no tener un constructor iría en contra de la práctica OO de asegurar activamente una invariante, C ++ tomó esto como que significaba que había un constructor sin parámetros predeterminado que actuaba como si tuviera un cuerpo vacío.

Todos los C structsahora eran C ++ válidos structs, (lo que significaba que eran lo mismo que C ++ classescon todo, miembros y herencia, público) tratados desde el exterior como si tuviera un único constructor sin parámetros.

Sin embargo, si puso un constructor en una classo struct, entonces estaba haciendo las cosas de la manera C ++ / OO en lugar de la forma C, y no había necesidad de un constructor predeterminado.

Dado que sirvió como una abreviatura, la gente siguió usándolo incluso cuando la compatibilidad no era posible de otra manera (usaba otras características de C ++ que no estaban en C).

Por lo tanto, cuando apareció Java (basado en C ++ de muchas maneras) y luego C # (basado en C ++ y Java de diferentes maneras), mantuvieron este enfoque como algo a lo que los codificadores ya pueden estar acostumbrados.

Stroustrup escribe sobre esto en su lenguaje de programación The C ++ y aún más, con un mayor enfoque en los "porqués" del lenguaje en The Design and Evolution of C ++ .

=== Respuesta original ===

Digamos que esto no sucedió.

Digamos que no quiero un constructor sin parámetros, porque no puedo poner mi clase en un estado significativo sin uno. De hecho, esto es algo que puede suceder structen C # (pero si no puede hacer un uso significativo de todos ceros y nulos structen C #, en el mejor de los casos, está utilizando una optimización no visible públicamente y, de lo contrario, tiene un falla de diseño en el uso struct).

Para que mi clase pueda proteger a sus invariantes, necesito una removeDefaultConstructorpalabra clave especial . Por lo menos, necesitaría crear un constructor privado sin parámetros para asegurarme de que ningún código de llamada llame al valor predeterminado.

Lo que complica aún más el lenguaje. Mejor no hacerlo.

En general, es mejor no pensar en agregar un constructor como eliminar el valor predeterminado, mejor pensar en no tener ningún constructor como azúcar sintáctica para agregar un constructor sin parámetros que no haga nada.


1
Y una vez más, veo que alguien siente que esta es una respuesta lo suficientemente mala como para rechazarla, pero no podría molestarse en iluminarme a mí ni a nadie más. Lo que podría ser, ya sabes, útil.
Jon Hanna

Lo mismo en mi respuesta. Bueno, no veo nada malo aquí, así que +1 de mi parte.
Botz3000

@ Botz3000 No me importa la puntuación, pero si tienen una crítica, prefiero leerla. Aún así, me hizo pensar en algo para agregar a lo anterior.
Jon Hanna

1
Y de nuevo con el voto negativo sin ninguna explicación. Por favor, si me falta algo tan obvio que no requiere explicación, simplemente asuma que soy estúpido y hágame el favor de explicarlo de todos modos.
Jon Hanna

1
@jogojapan Tampoco soy experto, pero el tipo que es, al ser el que tomó la decisión, escribió sobre eso, así que no tengo que serlo. Por cierto, es un libro muy interesante; poco sobre tecnología de bajo nivel y muchas decisiones de diseño, con partes divertidas como el discurso de una persona que dice que deberías donar un riñón antes de proponer una nueva función (pensarías muy duro, y solo lo harías dos veces) justo antes de su discurso presentando ambas plantillas y excepciones.
Jon Hanna

13

El constructor sin parámetros predeterminado se agrega si no hace nada usted mismo para tomar el control sobre la creación de objetos. Una vez que haya creado un solo constructor para tomar el control, el compilador "retrocede" y le permite tener el control total.

Si no fuera así, necesitaría alguna forma explícita de deshabilitar el constructor predeterminado si solo desea que los objetos sean construibles a través de un constructor con parámetros.


De hecho tienes esa opción. Hacer que el constructor sin parámetros sea privado.
mw_21

55
Eso no es lo mismo. Cualquier método de la clase, incluidos los métodos estáticos, podría llamarlo. Prefiero tenerlo totalmente inexistente.
Anders Abel

3

Es una función de conveniencia del compilador. Si define un constructor con parámetros pero no define un constructor sin parámetros, la posibilidad de que no desee permitir un constructor sin parámetros es mucho mayor.

Este es el caso de muchos objetos que simplemente no tienen sentido inicializar con un constructor vacío.

De lo contrario, tendría que declarar un constructor privado sin parámetros para cada clase que desee restringir.

En mi opinión, no es un buen estilo permitir un constructor sin parámetros para una clase que necesita parámetros para funcionar.


3

Creo que la pregunta debería ser al revés: ¿por qué no necesita declarar un constructor predeterminado si no ha definido ningún otro constructor?

Un constructor es obligatorio para las clases no estáticas.
Así que creo que si no ha definido ningún constructor, el constructor predeterminado generado es solo una característica conveniente del compilador de C #, además su clase no sería válida sin un constructor. Entonces, no hay nada malo en generar implícitamente un constructor que no hace nada. Ciertamente se ve más limpio que tener constructores vacíos por todas partes.

Si ya ha definido un constructor, su clase es válida, entonces, ¿por qué el compilador debería suponer que desea un constructor predeterminado? ¿Qué pasa si no quieres uno? ¿Implementar un atributo para decirle al compilador que no genere ese constructor predeterminado? No creo que sea una buena idea.


1

El constructor predeterminado solo se puede construir cuando la clase no tiene un constructor. Los compiladores están escritos de tal manera que solo se proporcionan como un mecanismo de respaldo.

Si tiene un constructor parametrizado, es posible que no desee que se cree un objeto utilizando el constructor predeterminado. Si el compilador hubiera proporcionado un constructor predeterminado, habría tenido que escribir un constructor sin argumentos y hacerlo privado para evitar que los objetos se creen sin argumentos.

Además, habría mayores posibilidades de que olvides deshabilitar o 'privatizar' el constructor predeterminado y, por lo tanto, causar un posible error funcional difícil de detectar.

Y ahora tiene que definir explícitamente un constructor sin argumentos si desea que se cree un objeto de forma predeterminada o pasando parámetros. Esto se verifica fuertemente y el compilador se queja de lo contrario, asegurando así que no haya escapatorias aquí.


1

Premisa

Este comportamiento puede verse como una extensión natural de la decisión de las clases de tener un constructor público sin parámetros predeterminado . En función de la pregunta que se nos formula, tomamos esta decisión como premisa y asumimos que no la estamos cuestionando en este caso.

Formas de eliminar el constructor predeterminado

Se deduce que debe haber una manera de eliminar el constructor público sin parámetros predeterminado. Esta eliminación podría llevarse a cabo de las siguientes maneras:

  1. Declarar un constructor sin parámetros no público
  2. Eliminar automáticamente el constructor sin parámetros cuando se declara un constructor con parámetros
  3. Alguna palabra clave / atributo para indicar al compilador que elimine el constructor sin parámetros (lo suficientemente incómodo como para que sea fácil descartarlo)

Seleccionando la mejor solución

Ahora nos preguntamos: si no hay un constructor sin parámetros, ¿por qué debe ser reemplazado? y ¿Bajo qué tipos de escenarios querríamos eliminar el constructor público sin parámetros predeterminado?

Las cosas comienzan a ponerse en su lugar. En primer lugar, debe reemplazarse con un constructor con parámetros o con un constructor no público. En segundo lugar, los escenarios en los que no desea un constructor sin parámetros son:

  1. No queremos que la clase sea instanciada en absoluto, o queremos controlar la visibilidad del constructor: declarar un constructor no público
  2. Queremos forzar los parámetros que se proporcionarán en la construcción: declarar un constructor con parámetros

Conclusión

Ahí lo tenemos, exactamente las dos formas en que C #, C ++ y Java permiten la eliminación del constructor público sin parámetros predeterminado.


Claramente estructurado y fácil de seguir, +1. Pero con respecto al (3.) anterior: no creo que una palabra clave especial para la eliminación del constructor sea una idea tan incómoda, y de hecho C ++ 11 se ha introducido = deletepara este propósito.
jogojapan

1

Creo que esto es manejado por el compilador. Si abre el .netensamblaje ILDASM, verá el constructor predeterminado, incluso si no está en el código. Si define un constructor parametrizado, no se verá el constructor predeterminado.

En realidad, cuando define la clase (no estática), el compilador proporciona esta característica pensando que solo creará una instancia. Y si desea realizar una operación específica, seguramente tendrá su propio constructor.


0

Es porque cuando no define un constructor, el compilador genera automáticamente un constructor para usted que no toma ningún argumento. Cuando quieres algo más de un constructor, lo anulas. Esto NO es sobrecarga de funciones. Entonces, el único constructor que el compilador ve ahora es su constructor que toma un argumento. Para contrarrestar este problema, puede pasar un valor predeterminado si el constructor se pasa sin ningún valor.


0

Una clase necesita un constructor. Es requisito obligatorio.

  • Si no crea uno, se le dará automáticamente un constructor sin parámetros.
  • Si no desea un constructor sin parámetros, debe crear el suyo propio.
  • Si necesita ambos, constructor sin parámetros y constructor basado en parámetros, puede agregarlos manualmente.

Contestaré su pregunta con otra, ¿por qué queremos siempre un constructor sin parámetros predeterminado? hay casos en los que esto no se desea, por lo que el desarrollador tiene el control para agregarlo o eliminarlo según sea necesario.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.