Creación de componentes VCL (II) - Punteros en Object Pascal
6 - Punteros en Object Pascal
Como norma general, para definir un puntero basta con declarar una variable, y anteponer el símbolo ^ al tipo de dato. Por ejemplo:
var
punteroInteger: ^Integer;
punteroChar: ^char;
Para definir punteros a funciones, la cosa cambia un poco, ya que hay que definir los parámetros que acepta la función. Basta con declarar una variable de tipo “procedure (parámetros)” o “function (parámetros): retorno”. Por ejemplo:
var punteroProc: procedure(parametro: boolean; otro: string); punteroFunc: function(const parametro: char; var otro: char): boolean;
Si vivimos en el mundo orientado a objetos, las cosas cambian ligeramente, porque tenemos que tener en cuenta un pequeño detalle: en vez de funciones utilizamos métodos. Para definir un puntero a método, basta con definir un puntero como si fuera a una función normal, y añadir la palabra “of object” al final. Por ejemplo:
var punteroMetodo: procedure(parametro, otro: string) of object; punteroMetodoFunc: function(const parametro, otro: char): boolean of object;
Esto es todo lo que se puede decir sobre la declaración de punteros.
Sobre su uso, hay que conocer dos operadores:
- @variable: que nos devuelve la dirección de una variable
- puntero^: que nos devuelve el valor de la variable apuntada por el puntero
A continuación, podemos ver un ejemplo de uso de punteros.
var ptrInteger: ^Integer; ptrChar: ^char; unInteger: integer; unChar: char; begin unInteger := 1; unChar := ‘a’; // se almacena en los punteros, las direcciones de // las variables que queremos apuntar ptrInteger := @unInteger; ptrChar := @unChar; ptrInteger^ := 2; // unInteger ahora vale 2 ptrChar^ := ‘b’; // unChar ahora vale ‘b’ end;
Con funciones, la situación es parecida, tal y como podéis ver en el siguiente código:
function UnaFuncion(valor: boolean; otroValor: string): integer; begin // ... end; function OtraFuncion(valor: boolean; otroValor: string): integer; begin // ... end; function UsandoPunteros(); var ptrFuncion: (valor: boolean; otroValor: string): integer; begin ptrFuncion := @UnaFuncion; // ambas llamadas son equivalentes. UnaFuncion(true, ‘hola’); punteroFuncion(true, ‘hola’); // apunto a otra dirección, donde está otra función ptrFuncion := @OtraFuncion; // utilizo el mismo puntero para llamar a otra función OtraFuncion(false, ‘adiós’); punteroFuncion(false, ‘adiós’); end;
Pues sí, un evento no es más que un atributo de tipo “puntero a método”, que se accede a través de una propiedad de lectura y escritura en la sección published.
El primer paso será definir un nuevo tipo de dato, que sea un puntero a un método, del siguiente modo:
type TEventoConversion = procedure(Sender: TObject; nuevoResultado: float) of object;
Con esto, hemos definido un nuevo tipo de dato que representa a un puntero a método. Posteriormente, podremos utilizarlo para declarar el evento de nuestro componente. El método “apuntado” utiliza dos argumentos: un objeto descendiente de TObject y un valor de tipo float.
Lo siguiente que tenemos que hacer es definir un atributo de este tipo, es decir: definir un atributo de tipo puntero a método. En el siguiente listado podéis ver cómo quedaría la sección private de nuestro componente después de declarar nuestro nuevo atributo de tipo puntero, que nos servirá para almacenar la dirección de un método. Este método que apuntaremos, será el que codifique el programador cuando haga doble clic sobre el evento.
TConversorMonedas = class(TComponent) private // declaración del puntero que contendrá la función del evento FOnConversion: TEventoConversion; FValorConvertir: float; FValorConvertido: float; FMonedaConvertir: TTipoMoneda; FMonedaConvertido: TTipoMoneda; ...
El siguiente paso es la definición de una propiedad, en la sección published, que nos permita crear o modificar el evento en tiempo de diseño. Podéis ver la nueva sección published en el siguiente código:
published property ValorConvertir: float read FValorConvertir write SetValorConvertir; property ValorConvertido: float read FValorConvertido; property MonedaConvertir: TTipoMoneda read FMonedaConvertir write SetMonedaConvertir; property MonedaConvertido: TTipoMoneda read FMonedaConvertido write FMonedaConvertido // declaración del evento property OnConversion: TEventoConversion read FOnConversion write FOnConversion
Una vez que hemos declarado el evento, podemos registrar el componente para ver si aparece en el Inspector de Objetos, como cualquier otro evento, tal y como podéis ver en la imagen de la derecha.
Bien, ya tenemos nuestro evento, y si hacemos doble clic sobre él, veremos que el entorno de Delphi nos crea un nuevo método, en la clase que representa al formulario con el siguiente aspecto:
procedure TForm1.ConversorMonedas1Convertido(Sender: TObject; nuevoResultado: float); begin end;
En realidad, al hacer doble clic, lo que ha ocurrido es que Delphi nos ha definido un nuevo método en la clase del formulario, y ha asignado la dirección de este método a la propiedad OnConvertido del componente ¡Y todo esto de un simple ratonazo!
Esta es una de las grandes maravillas de Delphi, y otros lenguajes han utilizado la misma idea (como C# de Microsoft).
Pero nuestro componente no está completo, ya que el nuevo evento OnConvertido no se ejecutará nunca, porque nadie ha dicho que se ejecute ¿no?
Simplemente debemos hacer una llamada al evento, cada vez que se produzca una conversión. Esto, como sabemos, se produce dentro del procedimiento “Convertir”, así que, justo antes de asignar el resultado final, debemos llamar al evento. Hay que tener cuidado de llamar al evento sólo cuando el puntero tenga un valor distinto de nil ya que si tiene este valor, significará que el programador no ha hecho doble clic para codificar el evento, por lo que no debemos realizar la llamada, o se producirá un error de memoria. Esto lo podemos hacer con una simple comparación (if FOnConvertido <> nil then...) o bien utilizando la función Assigned, que sirve precisamente para esto: para ver si un puntero está apuntado (asignado) a algún sitio: if Assigned(FOnConvertido) then... Podéis ver el código final del método “Convertir” en el siguiente listado:
procedure TConversorMonedas.Convertir(); const FactorAEuro: array[TTipoMoneda] of float = ( 1, 1.10, 2, 0.5, 0.18, 166.386 ); var aux: float; begin // se calcula utilizando el euro como referencia. aux := FValorConvertir / FactorAEuro[FMonedaConvertir]; // ahora aux contiene el valor origen en euros. Se pasa // a la moneda destino aux := aux * FactorAEuro[FMonedaConvertido]; // llamar al evento sólo si ha sido codificado if Assigned(FOnConvertido) then FOnConvertido(Self, aux); FValorConvertido := aux; end;
Bien, pues ya sabemos cómo definir eventos en nuestros propios componentes. Ahora nuestro conversor de monedas ha quedado más completo, y sabemos que el programador que lo utilice podrá realizar sus acciones en el nuevo evento.
|
Opiniona sobre 'Creación de componentes VCL (II) - Punteros en Object Pascal' (0)
Opina sobre este curso gratis |

