2011-11-07 3 views
8

Siempre he tenido la impresión de que los objetos en Delphi son, de hecho, referencias a ubicaciones de memoria, que a su vez he imaginado que estaban almacenadas como variables de puntero.¿Por qué TValue.Make requiere un puntero a una referencia de objeto?

Ahora, quiero hacer un TValue de un objeto. Considere esto:

TValue.Make(AObject, TypeInfo(TMyObject), val); 

donde val: TValue. Esto no funcionará De hecho, el uso posterior de val dará lugar a una infracción de acceso. Sin embargo, si utilizamos el operador de dirección de la siguiente manera:

TValue.Make(@AObject, TypeInfo(TMyObject), val); 

todo está bien. Para mí, esto fue inesperado, ya que pensé que AObject era (bajo el capó) de hecho un puntero. ¿Estoy equivocado o es esto una peculiaridad con el método TValue.Make? ¿Puede alguien alumbrarme?

Respuesta

9
procedure Foo; 
var 
    I: Integer; // value type 
    O: TObject; // reference type 
begin 
    @I; // Get a pointer to I 
    O := TObject.Create; 
    @O; // Get a pointer to the memory "behind" the reference O 
end; 
  • El @I ubicación, así como la ubicación de O (la referencia) está en la pila.
  • La ubicación @O por otro lado está en el montón.

Normalmente eso no importa mucho, porque el compilador sabe cuándo eliminar la referencia y cuándo no. En el caso de TValue.Make, la función toma un puntero

  • Cuando especifique Make(O..., el compilador lanzará con fuerza la referencia a un puntero (que apunta a la pila).
  • Cuando especifica Make(@O..., el compilador desreferenciará primero y luego creará un puntero a la ubicación en el montón.

Así que tiene que dar una indicación al compilador en este caso, porque no sabe qué tipo de puntero espera TValue.Make.

+0

muchas gracias por un claro y sucinta explicación! – conciliator

+0

Sutil y fundamental ... ;-) +1 –

1

En su ejemplo, AObject es una referencia a un objeto, no al objeto en sí. Esta es la manera de declarar la referencia del objeto en Delphi en lugar de otro idioma donde debe agregar explícitamente la referencia o el puntero al objeto.

Así AObject o @AObject debería funcionar de la misma, en su caso, pero como TValue.Make() reciben un puntero a un búfer en el primer parámetro, debe proporcionar Addr(AObject) o @AObject a la función.

+1

Pero @conciliator afirma que 'AObject' y' @ AObject' funcionan de manera diferente para él, ¿parece contradecir su respuesta? – Zruty

+0

@TridentT: Tengo que admitir que todavía estoy confundido. Si 'AObject' y' @ AObject' son ambos punteros, ¿por qué uno funcionaría y no el otro? Intuitivamente, pensaría en '@ AObject' como un puntero a un puntero. (Por ejemplo, digamos que me gustaría hacer un TValue desde un int 'intvar'. En tal caso,' @ intvar' tiene mucho sentido, pero el caso del objeto todavía me desconcierta ...) – conciliator

2

El argumento ABuffer que pasa a TValue.Make es el puntero al valor que desea almacenar dentro de TValue. No importa si el tipo en sí es un tipo de puntero o no. Entonces debe pasar la referencia a AObject incluso si AObject es también un puntero.

En el ejemplo se registró yo preferiría utilizar el TValue.From <T> método:

val := TValue.From<TMyObject>(AObject); 

Si typeinfo no se conoce en tiempo de compilación tiene que usar TValue.Make - de lo contrario TValue.From <T> es más fácil de usar.

Cuestiones relacionadas