language design - programming - ¿Por qué C#y Java se molestan con el "nuevo" operador?
how to make a compiler for your own language (9)
¿Por qué existe el nuevo operador en idiomas modernos como C # y Java? ¿Es puramente una característica del código de autoedición, o sirve para algún propósito real?
Por ejemplo, el siguiente ejemplo:
Class1 obj = new Class1();
Class1 foo()
{
return new Class1();
}
Es tan fácil de leer como la forma más pitonesca de escribirlo:
Class1 obj = Class1();
Class1 foo()
{
return Class1();
}
EDITAR: Cowan dio en el clavo con la aclaración de la pregunta: ¿Por qué eligieron esta sintaxis?
¿Has considerado usar métodos estáticos?
class Class1
{
public static Class1 Instance() { return new Class1(); }
}
Class1 obj = Class1.Instance();
Class1 foo()
{
return Class1.Instance();
}
- Es una función de autoedición.
- Es una manera de hacer posible el nombre de un método "Clase 1" en alguna otra clase
Además de los comentarios sobre AFAIK, estaban planeando eliminar una nueva palabra clave para Java 7 en los primeros borradores. Pero luego lo cancelaron.
El nuevo operador asigna la memoria para el objeto (s), que es su propósito; como dices, también documenta por ti mismo qué instancia (es) (es decir, una nueva) con la que estás trabajando
El nuevo operador en C # se asigna directamente a la instrucción IL llamada newobj
que realmente asigna el espacio para las variables del nuevo objeto y luego ejecuta el constructor (llamado .ctor en IL). Al ejecutar el constructor, al igual que C ++, se pasa una referencia al objeto inicializado como primer parámetro invisible (como este llamado ).
La convención tipo llamada permite que el tiempo de ejecución cargue y JITE todo el código en memoria para una clase específica solo una vez y lo reutilice para cada instancia de la clase.
Java puede tener un código de operación similar en su lenguaje intermedio, pensó que no estoy lo suficientemente familiar como para decirlo.
La utilidad es la documentación: es más fácil distinguir las creaciones de objetos de las invocaciones de métodos que en Python.
La razón es histórica, y viene directamente de la sintaxis de C ++. En C ++, "Class1 ()" es una expresión que crea una instancia de Class1 en la pila . Por ejemplo: vector a = vector (); En este caso, se crea un vector y se copia al vector a (un optimizador puede eliminar la copia redundante en algunos casos).
En cambio, "new Class1 ()" crea una instancia de Class1 en el montón , como en Java y C #, y le devuelve un puntero , con una sintaxis de acceso diferente, a diferencia de Java y C ++. En realidad, el significado de nuevo se puede redefinir para usar cualquier asignador de propósito especial, que todavía debe hacer referencia a algún tipo de pila, de modo que el objeto obtenido pueda devolverse por referencia.
Además, en Java / C # / C ++, Class1 () por sí solo podría referirse a cualquier método / función, y sería confuso. La convención de codificación Java en realidad evitaría eso, ya que requieren que los nombres de las clases comiencen con una letra mayúscula y los nombres de los métodos comiencen con una letra minúscula, y probablemente esa es la forma en que Python evita la confusión en este caso. Un lector espera que "Class1 ()" cree un objeto, "class1 ()" sea una invocación de función, y "x.class1 ()" sea una invocación de método (donde ''x'' puede ser ''self'').
Finalmente, dado que en Python optaron por hacer que las clases fueran objetos, y objetos invocables en particular, se permitiría la sintaxis sin ''nuevo'', y sería inconsistente permitir también tener otra sintaxis.
Class1 obj = Class1();
En C # y Java, necesita la palabra clave "nueva" porque sin ella trata "Class1 ()" como una llamada a un método cuyo nombre es "Class1".
La razón por la que Java lo eligió fue porque la sintaxis era familiar para los desarrolladores de C ++. La razón por la que C # lo eligió fue porque era familiar para los desarrolladores de Java.
La razón por la que se usa el new
operador en C ++ es probablemente porque con la administración manual de la memoria es muy importante dejar en claro cuándo se asigna la memoria. Si bien la sintaxis pitonesca podría funcionar, es menos obvio que se asigna la memoria.
C ++ ofrece a los programadores la opción de asignar objetos en el montón o en la pila.
La asignación basada en pila es más eficiente : la asignación es más económica, los costos de desasignación son verdaderamente cero, y el lenguaje proporciona asistencia para delimitar los ciclos de vida de los objetos, reduciendo el riesgo de olvidarse de liberar el objeto.
Por otro lado, en C ++, debe tener mucho cuidado cuando publique o comparta referencias a objetos basados en pila, ya que los objetos basados en pila se liberan automáticamente cuando se desenrolla el marco de la pila, lo que lleva a punteros colgantes.
Con el new
operador , todos los objetos se asignan en el montón en Java o C #.
Class1 obj = Class1 ();
En realidad, el compilador intentaría encontrar un método llamado Class1()
.
Por ejemplo, el siguiente es un error común de Java:
public class MyClass
{
//Oops, this has a return type, so its a method not a constructor!
//Because no constructor is defined, Java will add a default one.
//init() will not get called if you do new MyClass();
public void MyClass()
{
init();
}
public void init()
{
...
}
}
Nota: "todos los objetos se asignan en el montón" no significa que la asignación de pila no se usa de vez en cuando.
Por ejemplo, en Java, la optimización de Hotspot como el análisis de escape usa la asignación de la pila.
Este análisis realizado por el compilador de tiempo de ejecución puede concluir, por ejemplo, que un objeto en el montón se referencia solo localmente en un método y ninguna referencia puede escapar de este ámbito. Si es así, Hotspot puede aplicar optimizaciones en tiempo de ejecución. Puede asignar el objeto en la pila o en los registros en lugar de en el montón.
Sin embargo, dicha optimización no siempre se considera decisiva ...