Universidad de Costa Rica
Escuela de Ciencias de la
Computación e Informática
Profesor Adolfo Di Mare
CI-1402
I Semestre 1998
[<=] [home] [<>] [\/] [=>]
CI-1402 Organización de Lenguajes de Programación

Tarea #4 [solución]

Simulación de parametrización con herencia

      En su artículo Genericity vs Inheritance [Mey­86], Bertrand Meyer, creador del lenguaje Eiffel, usa su lenguaje para mostrar que es posible simular la parametrización por medio de la herencia.

      En esta tarea programada usted hará lo propio, pero usará el lenguaje Turbo Pascal, en cualquiera de sus versiones, o el lenguaje C++. (Por supuesto, no puede usar plantillas si usa C++).

      Implemente en su programa el equivalente del procedimiento Swap() de la Figura /24/ y también implemente las clases RING y MATRIX[T] de la Figura /31/. Haga versiones de la clase MATRIX[] para los tipos INTEGER y BOOLEAN. Si quiere obtener puntaje extra, también implemente la clase STACK[T] de la Figura /35/.

      Entregue una documentación suscinta junto a su programa, y muestre que funciona con sus datos de prueba. El objetivo de esta tarea es que usted experimente para que comprenda las ventajas de usar lenguaje que cuenta con parametrización y herencia.

      Entregue su tarea en dos partes. Al final de la primera semana, debe entregar un listado que contenga la declaración de tipos los que usará en su programa, y al final de la segunda semana entregue su programa completo

Algunas notas de implementación

      No es sencillo invocar al constructor de un objeto cuando solo se tiene un puntero hacia el constructor. Más aún, no hay una forma portable de hacerlo. En el programa InitDone.pas se muestra como lograrlo en el contexto de Turbo Pascal de 16 bits.

PROGRAM InitDone;  { (c) 1998 adolfo@di-mare.com }

TYPE
  TObj = OBJECT
    CONSTRUCTOR Init;
    DESTRUCTOR  Done;
  END; { TObj }

TYPE
  TFar_Pointer = RECORD
    CASE INTEGER OF
      1:(ofs, seg: WORD;   ); { véalo como 2 palabras }
      2:(p       : POINTER;); { véalo como puntero    }
  END;

CONSTRUCTOR TObj.Init;
BEGIN
  WriteLn('TObj.Init: ', LONGINT(@SELF));
END;  { TObj.Init }

DESTRUCTOR TObj.Done;
BEGIN
  WriteLn('TObj.Done: ', LONGINT(@SELF));
END;  { TObj.Done }

TYPE
  Pcnstr = PROCEDURE (vmtW: WORD; slf: POINTER);

CONST
  N = 5;
VAR
  i    : INTEGER;
  pObj : TFar_Pointer;
  pVMT : POINTER;
  pInit,
  pDone: POINTER;

BEGIN { InitDone }
  pVMT  := TypeOf(TObj);  { puntero a la VMT }
  pInit := @Tobj.Init;    { puntero al constructor }
  pDone := @Tobj.Done;    { puntero al destructor }

  System.GetMem(pObj.p, N*SizeOf(TObj));

  FOR i := 1 TO N DO BEGIN
    { invoca al constructor en el objeto pObj^ }
    Pcnstr(pInit)(Ofs(pVMT^), pObj.p);

    INC(pObj.ofs, SizeOf(TObj));
  END;

  FOR i := 1 TO N DO BEGIN
    DEC(pObj.ofs, SizeOf(TObj));

    { invoca al destructor en el objeto pObj^ }
    Pcnstr(pDone)(0, pObj.p);
  END;

  System.FreeMem(pObj.p, N*SizeOf(TObj));
END.  { InitDone }

      El tipo TObj tiene su constructor TObj.Init() y su destructor TObj.Done(). Pcnstr(vmtW, slf) es un tipo que sirve para invocar al constructor TObj.Init() por medio del puntero pInit (y también al destructor). Parece extraño que el constructor de TObj sea un procedimiento que recibe dos argumentos, y no solamente uno (SELF). El primer argumento "vmtW" es el número que será almacenado en el campo VMT (Virtual Method Table) del objeto [BI­88] (pp 104).

      Al principio del programa se graba en los punteros pVMT, pInit y pDone las direcciones de la VMT de TObj, la dirección de su constructor y la de su destructor. El tipo TFar_Pointer es un artilugio que permite incrementar el puntero pObj, que apunta a un vector, para recorrer los N elementos de tipo TObj que contiene.

      Luego, de un solo tirón, se adquiere suficiente memoria dinámica para almacenar el vector de N elementos, para lo que se invoca al procedimiento System.GetMem(). En los dos ciclos FOR que siguen, se construye y destruye cada uno de los elmentos del vector.

      Para invocar al constructor, e inicializar cada objeto en el vector, se usa una transferencia al tipo Pcnstr, y luego se agregan los argumentos que el constructor necesita:
      Pcnstr(pInit)(Ofs(pVMT^), pObj.p);
El resultado de aplicarle la función Ofs() al puntero pVMT es obtener el valor para el argumento "vmtW" que, contrario a lo que parece, no un puntero, sino un número entero.

      Para invocar al destructor se hace lo mismo, pero en lugar de pasar el valor Ofs(pVMT^), como en el constructor, hay que pasar el valor cero (0). De esta manera se evita que la memoria que SELF ocupa sea retornada al manejador de memoria dinámica, pues cuando el destructor es invocado con un valor no nulo en el parámetro "pVMT" es porque se ha usado la sintáxis extendida de System.DISPOSE():
      System.DISPOSE(p, TObj.Done);

      Al final, después de destruir todos los objetos del vector (en el orden inverso de su creación), se devuelve toda la memoria dinámica obtenida al principio invocando a System.DISPOSE().

Tiempo de entrega: 2 Semanas
Modalidad: Individual

[BI­88] Borland International: Turbo Pascal version 5.5 Object Oriented Programming Guide, Borland International, California (U.S.A.), 1988.
[Mey­86] Meyer, Bertrand: Genericity vs Inheritance, OOPSLA'86 Conference Proceedings, pp [391­405], Portland, Oregon, 1986.

[mailto:] Adolfo Di Mare <adolfo@di-mare.com>.
Copyright © 1998
Derechos de autor reservados © 1998
[home] <> [/\]