6 - Punteros en Object Pascal

[editar]
Curso gratis creado por José Manuel. Extraido de: http://www.lawebdejm.com
30 de Noviembre de 1999
En Delphi es sencillo definir punteros a variables y a funciones.
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;
 
 
Los eventos son propiedades de tipo puntero

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.

[editar]

Sé el primero en opinar


Cursos gratis relacionados con 'Creación de componentes VCL: Eventos en los componentes'

Vamos a profundizar algo más en la programación de componentes, centrándonos en los eventos y... Más »
En los últimos años, el desarrollo basado en componentes se ha convertido en la técnica... Más »
Un repaso a los componentes gráficos, aquellos en los que nos tenemos que encargar de... Más »
En los últimos años, el desarrollo basado en componentes se ha convertido en una de... Más »
Ahora que ya sabemos programar nuestros componentes no visuales, vamos a aprender todo lo necesario... Más »

Autor y licencia de 'Creación de componentes VCL: Eventos en los componentes'


Curso gratis de José Manuel. Extraido de: http://www.lawebdejm.com CopyLeft
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.
Wikilearning tiene permiso expreso por escrito de los autores para publicar los contenidos que ha extraído de otras webs, incluyendo su uso comercial.