pointers - paso - parametros por valor y referencia php
¿Cuál es el modelo de valor vs. referencia en Nimrod? (2)
NOTA: No estoy preguntando acerca de la diferencia entre puntero y referencia, y para esta pregunta es completamente irrelevante.
Una cosa que no pude encontrar explícitamente: ¿qué modelo usa Nimrod?
Como C ++, donde tiene valores y con los new
, crea punteros a datos (en tal caso, la variable podría mantener el puntero a un puntero a un puntero a ... a datos).
¿O como C #, donde tiene tipos de POD como valores, pero los objetos definidos por el usuario con referencia (implícitamente)?
Descubrí que solo la desreferenciación es automática, como en Go.
Expresar de otro modo. Usted define su nuevo tipo, digamos Student
(con nombre, universidad, dirección). Usted escribe:
var student ...?
- para hacer que el
student
mantenga datos reales (de tipo / clase deStudent
) - Para que el
student
mantenga un puntero a los datos. - para hacer que el
student
mantenga un puntero a un puntero a los datos
¿O algunos de esos puntos son imposibles?
Por defecto el modelo es de pasar datos por valor. Cuando creas una var de un tipo específico, el compilador asignará en la pila el espacio requerido para la variable. Lo que se espera, ya que Nim compila a C, y los tipos complejos son solo estructuras. Pero como en C o C ++, también puedes tener punteros. Existe la palabra clave ptr
para obtener un puntero no seguro, principalmente para interactuar con el código C, y hay una ref
para obtener una referencia segura recogida de basura (ambas documentadas en la sección Referencias y tipos de punteros del manual de Nim).
Sin embargo, tenga en cuenta que incluso cuando especifica un proc
para pasar una variable por valor, el compilador es libre de decidir pasarlo internamente por referencia si considera que puede acelerar la ejecución y es seguro al mismo tiempo. En la práctica, la única vez que he usado referencias es cuando estaba exportando tipos Nim a C y tenía que asegurarme de que tanto C como Nim apuntaban a la misma memoria. Recuerde que siempre puede verificar el código C generado en el directorio nimcache
. Verá entonces que un parámetro var
en un proceso es solo un puntero a su estructura C.
Aquí hay un ejemplo de un tipo con constructores que se crearán en la pila y se pasarán por valor, y el puntero correspondiente como la versión:
type
Person = object
age: int
name: string
proc initPerson(age: int, name: string): Person =
result.age = age
result.name = name
proc newPerson(age: int, name: string): ref Person =
new(result)
result.age = age
result.name = name
when isMainModule:
var
a = initPerson(3, "foo")
b = newPerson(4, "bar")
echo a.name & " " & $a.age
echo b.name & " " & $b.age
Como puedes ver, el código es esencialmente el mismo, pero hay algunas diferencias:
- La forma típica de diferenciar la inicialización es usar init para los tipos de valor y nuevo para los tipos de referencia. Además, tenga en cuenta que la biblioteca estándar de Nim confunde esta convención, ya que parte del código es anterior (por ejemplo, newStringOfCap no devuelve una referencia a un tipo de cadena).
- Dependiendo de lo que realmente hagan sus constructores , la versión
ref
permite devolver un valornil
, que puede tratar como un error, mientras que el constructor de valores lo obliga a generar una excepción o cambiar el constructor para usar la forma var mencionada a continuación para que Puede devolver un bool indicando éxito. El fracaso tiende a ser tratado de diferentes maneras. En lenguajes similares a C, hay una sintaxis explícita para acceder al valor de memoria de un puntero o al valor de memoria señalado por él (eliminación de referencias). En Nim también hay, y es la notación del subíndice vacío (
[]
). Sin embargo, el compilador intentará colocarlos automáticamente para evitar saturar el código. Por lo tanto, el ejemplo no los usa. Para probar esto puedes cambiar el código para leer:echo b[].name & " " & $b[].age
Que funcionará y compilará como se espera. Pero el siguiente cambio producirá un error del compilador porque no puede eliminar la referencia a un tipo que no sea de referencia:
echo a[].name & " " & $a[].age
La tendencia actual en la comunidad Nim es deshacerse de los prefijos de una sola letra para diferenciar los tipos de valor frente a los de referencia. En la convención anterior, tendría un
TPerson
y un alias para el valor de referencia comoPPerson = ref TPerson
. Puedes encontrar una gran cantidad de código aún utilizando esta convención.- Dependiendo de qué deben hacer exactamente su objeto y su constructor, en lugar de que un
initPerson
devuelva el valor, también podría tener uninit(x: var Person, ...)
. Pero el uso de la variable deresult
implícita permite que el compilador optimice esto, por lo que es mucho más una preferencia de gusto o requisitos de pasar unbool
a la persona que llama.
Puede ser cualquiera.
type Student = object ...
es aproximadamente equivalente a
typedef struct { ... } Student;
en C, mientras
type Student = ref object ...
o
type Student = ptr object ...
es aproximadamente equivalente a
typedef struct { ... } *Student;
en C (con ref
denota una referencia que es rastreada por el recolector de basura, mientras que ptr
no es rastreada).