Programación en Ada/Tipos/Punteros a objetos

← Tipos/Registros variantes Punteros a objetos Tipos/Punteros a subprogramas →


Un nombre está ligado a un objeto desde su declaración hasta que el flujo del programa deja la unidad que contenía su declaración. Sin embargo, los punteros o apuntadores (access) proporcionan acceso a otros objetos, que se pueden crear y destruir dinámicamente.

El nombre de access en vez del habitual pointer se debe a que al diseñar Ada se quería huir de la mala fama que los punteros habían creado gracias a lenguajes como C, en los que se puede usar los punteros de manera muy insegura. Los tipos acceso de Ada son más seguros entre otras cosas porque no existe la aritmética de punteros, especialmente peligrosa. Además el uso de punteros en Ada es prescindible en muchas más situaciones que en C.

Las variables de tipo puntero en Ada se inicializan implícitamente a null.

Ejemplos

editar

Por ejemplo, se puede definir un puntero a un tipo entero de esta manera:

type PEntero is access Integer;

En un ejemplo con registros:

declare
  type TBúfer is
    record
      Mensaje: String(1..4);
      Prioridad: Integer;
    end record;
type PTBúfer is access TBúfer;
Mensaje1, Mensaje2: PTBúfer;
begin
  Mensaje1 := new TBúfer;  -- Se crea un objeto de tipo TBúfer.
  Mensaje2 := new TBúfer'(Prioridad => 2, Mensaje => "Hola");
  -- Con all se puede desreferenciar el puntero.
  -- Mensaje1 es un puntero y Mensaje1.all es el registro.
  Mensaje1.all.Prioridad := 3;
  -- Sin embargo, al acceder a campos del registro la desreferenciación
  -- puede hacerse implícita y .all es opcional en esos casos:
  Mensaje1.Prioridad := 3;

end;

Es útil para implementar listas, colas, árboles y grafos. Por ejemplo:

declare
  -- TNodoÁrbolBinario se necesita para definir el puntero.
  type TNodoÁrbolBinario;  -- Se declara después.
  type PTNodoÁrbolBinario is access TNodoÁrbolBinario;
  type TNodoÁrbolBinario is
    record
      RamaIzda: PTNodoÁrbolBinario;
      Dato: Float;
      RamaDcha: PTNodoÁrbolBinario;
    end record;
   ÁrbolBinario: PTNodoÁrbolBinario;
begin
  -- Se crea la raíz del árbol binario.
  ÁrbolBinario := new TNodoÁrbolBinario'(null, 1.0, null);
end;

Liberación de memoria

editar

Cuando se quiera liberar la memoria dinámicamente, hay que hacer uso del procedimiento genérico Ada.Unchecked_Deallocation [1], el cual se instancia con los tipos de objeto y de puntero, y se le llama pasándole punteros. Por ejemplo:

with Ada.Unchecked_Deallocation;

procedure Ejemplo_Liberar_Memoria is
  type TVector is array (Integer range <>) of Float;
  type PVector is access TVector;
  PV: PVector;
  procedure Liberar_Vector is new Ada.Unchecked_Deallocation
     (TVector, PVector);
begin
  PV := new TVector(1..10);
  PV.all := (others => 0.0);
  -- ...
  Liberar_Vector (PV); -- La memoria es liberada y PV es ahora null
end Ejemplo_Liberar_Memoria;

El nombre de Unchecked_Deallocation viene del hecho de que no hay comprobación de que no queden punteros colgantes (dangling pointers), es decir que si se ha copiado el puntero en otra variable, después de llamar a Liberar_Vector el puntero copia está apuntando a una dirección de memoria no reservada y los efectos son imprevisibles, puesto que se puede haber reservado y se pude escribir o leer memoria que ya no pertenece a ese objeto.

Este sistema es similar al de C++ con new y delete. Un sistema de recolección de basura similar al de Java está previsto en el estándar, pero ningún compilador de Ada hasta el momento lo proporciona. Esto es debido a que aunque es un mecanismo más seguro, es menos eficiente y puede ser un problema para los sistemas de tiempo real por su impredictibilidad.

En Ada 95 existen métodos de gestión de memoria más seguros que el uso directo de Unchecked_Deallocation basados en los tipos controlados, algo semejante a lo que se consigue en C++ con constructores y destructores que manejan memoria dinámica.

Manual de referencia de Ada

editar