Pascal - Clases

Ha visto que los objetos Pascal exhiben algunas características del paradigma orientado a objetos. Implementan encapsulación, ocultación de datos y herencia, pero también tienen limitaciones. Por ejemplo, los objetos Pascal no participan en el polimorfismo. Por lo tanto, las clases se utilizan ampliamente para implementar un comportamiento orientado a objetos adecuado en un programa, especialmente el software basado en GUI.

Una clase se define casi de la misma manera que un objeto, pero es un puntero a un objeto en lugar del objeto en sí. Técnicamente, esto significa que la clase se asigna en el montón de un programa, mientras que el objeto se asigna en la pila. En otras palabras, cuando declaras una variable del tipo de objeto, ocupará tanto espacio en la pila como el tamaño del objeto, pero cuando declaras una variable del tipo de clase, siempre tomará el tamaño de un puntero. en la pila. Los datos de la clase real estarán en el montón.

Definición de clases Pascal

Una clase se declara de la misma forma que un objeto, utilizando la declaración de tipo. La forma general de una declaración de clase es la siguiente:

type class-identifier = class  
   private
      field1 : field-type;  
      field2 : field-type;  
        ...
   
   public
      constructor create();
      procedure proc1;  
      function f1(): function-type;
end;  
var classvar : class-identifier;

Vale la pena señalar los siguientes puntos importantes:

  • Las definiciones de clase deben incluirse únicamente en la parte de declaración de tipo del programa.

  • Una clase se define usando el class palabra clave.

  • Los campos son elementos de datos que existen en cada instancia de la clase.

  • Los métodos se declaran dentro de la definición de una clase.

  • Hay un constructor predefinido llamado Createen la clase Root. Cada clase abstracta y cada clase concreta es descendiente de Root, por lo que todas las clases tienen al menos un constructor.

  • Hay un destructor predefinido llamado Destroyen la clase Root. Cada clase abstracta y cada clase concreta es descendiente de Root, por lo que todas las clases tienen al menos un destructor.

Definamos una clase Rectangle que tiene dos miembros de datos de tipo entero: largo y ancho y algunas funciones miembro para manipular estos miembros de datos y un procedimiento para dibujar el rectángulo.

type
   Rectangle = class
   private
      length, width: integer;
   
   public
      constructor create(l, w: integer);
      procedure setlength(l: integer);
      function getlength(): integer;
      procedure setwidth(w: integer);
      function getwidth(): integer;
      procedure draw;
end;

Escribamos un programa completo que cree una instancia de una clase de rectángulo y dibuje el rectángulo. Este es el mismo ejemplo que usamos al hablar de Pascal Objects. Encontrará que ambos programas son casi iguales, con las siguientes excepciones:

  • Necesitará incluir la directiva {$ mode objfpc} para usar las clases.

  • Deberá incluir la directiva {$ m +} para usar constructores.

  • La instanciación de clases es diferente a la instanciación de objetos. Solo declarar la variable no crea espacio para la instancia, usará el constructor create para asignar memoria.

Aquí está el ejemplo completo:

{$mode objfpc} // directive to be used for defining classes
{$m+}		   // directive to be used for using constructor

program exClass;
type
   Rectangle = class
   private
      length, width: integer;
   
   public
      constructor create(l, w: integer);
      procedure setlength(l: integer);
      
      function getlength(): integer;
      procedure setwidth(w: integer);
      
      function getwidth(): integer;
      procedure draw;
end;
var
   r1: Rectangle;

constructor Rectangle.create(l, w: integer);
begin
   length := l;
   width := w;
end;

procedure Rectangle.setlength(l: integer);
begin
   length := l;
end;

procedure Rectangle.setwidth(w: integer);
begin
   width :=w;
end;

function Rectangle.getlength(): integer;
begin
   getlength := length;
end;

function Rectangle.getwidth(): integer;
begin
   getwidth := width;
end;

procedure Rectangle.draw;
var
   i, j: integer;
begin
   for i:= 1 to length do
   begin
      for j:= 1 to width do
         write(' * ');
      writeln;
   end;
end;

begin
   r1:= Rectangle.create(3, 7);
   
   writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
   r1.draw;
   r1.setlength(4);
   r1.setwidth(6);
   
   writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
   r1.draw;
end.

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Draw Rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw Rectangle: 4 by 6
* * * * * * 
* * * * * * 
* * * * * * 
* * * * * *

Visibilidad de los miembros de la clase

La visibilidad indica la accesibilidad de los miembros de la clase. Los miembros de la clase Pascal tienen cinco tipos de visibilidad:

No Señor Visibilidad y accesibilidad
1

Public

Estos miembros siempre son accesibles.

2

Private

Solo se puede acceder a estos miembros en el módulo o unidad que contiene la definición de clase. Se puede acceder a ellos desde dentro de los métodos de la clase o desde fuera de ellos.

3

Strict Private

Solo se puede acceder a estos miembros desde métodos de la propia clase. Otras clases o clases descendientes de la misma unidad no pueden acceder a ellas.

4

Protected

Esto es lo mismo que privado, excepto que estos miembros son accesibles para tipos descendientes, incluso si están implementados en otros módulos.

5

Published

Esto es lo mismo que un Public, pero el compilador genera información de tipo que se necesita para la transmisión automática de estas clases si el compilador está en el estado {$ M +}. Los campos definidos en una sección publicada deben ser de tipo de clase.

Constructores y destructores para clases Pascal

Los constructores son métodos especiales, que se llaman automáticamente cada vez que se crea un objeto. Así que aprovechamos al máximo este comportamiento inicializando muchas cosas mediante funciones constructoras.

Pascal proporciona una función especial llamada create () para definir un constructor. Puede pasar tantos argumentos como desee a la función constructora.

El siguiente ejemplo creará un constructor para una clase llamada Libros e inicializará el precio y el título del libro en el momento de la creación del objeto.

program classExample;

{$MODE OBJFPC} //directive to be used for creating classes
{$M+} //directive that allows class constructors and destructors
type
   Books = Class 
   private 
      title : String; 
      price: real;
   
   public
      constructor Create(t : String; p: real); //default constructor
      
      procedure setTitle(t : String); //sets title for a book
      function getTitle() : String; //retrieves title
      
      procedure setPrice(p : real); //sets price for a book
      function getPrice() : real; //retrieves price
      
      procedure Display(); // display details of a book
end;
var
   physics, chemistry, maths: Books;

//default constructor 
constructor Books.Create(t : String; p: real);
begin
   title := t;
   price := p;
end;

procedure Books.setTitle(t : String); //sets title for a book
begin
   title := t;
end;

function Books.getTitle() : String; //retrieves title
begin
   getTitle := title;
end;

procedure Books.setPrice(p : real); //sets price for a book
begin
   price := p;
end;

function Books.getPrice() : real; //retrieves price
begin
   getPrice:= price;
end;

procedure Books.Display();
begin
   writeln('Title: ', title);
   writeln('Price: ', price:5:2);
end;

begin 
   physics := Books.Create('Physics for High School', 10);
   chemistry := Books.Create('Advanced Chemistry', 15);
   maths := Books.Create('Algebra', 7);
   
   physics.Display;
   chemistry.Display;
   maths.Display;
end.

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Title: Physics for High School
Price: 10
Title: Advanced Chemistry
Price: 15
Title: Algebra
Price: 7

Al igual que el constructor implícito llamado create, también hay un método de destructor implícito, destroy, mediante el cual puede liberar todos los recursos utilizados en la clase.

Herencia

Las definiciones de clase de Pascal pueden heredar opcionalmente de una definición de clase principal. La sintaxis es la siguiente:

type
childClas-identifier = class(baseClass-identifier) 
< members >
end;

El siguiente ejemplo proporciona una clase de novelas, que hereda la clase Libros y agrega más funcionalidad según el requisito.

program inheritanceExample;

{$MODE OBJFPC} //directive to be used for creating classes
{$M+} //directive that allows class constructors and destructors

type
   Books = Class 
   protected 
      title : String; 
      price: real;
   
   public
      constructor Create(t : String; p: real); //default constructor
      
      procedure setTitle(t : String); //sets title for a book
      function getTitle() : String; //retrieves title
      
      procedure setPrice(p : real); //sets price for a book
      function getPrice() : real; //retrieves price
      
      procedure Display(); virtual; // display details of a book
end;
(* Creating a derived class *)

type
   Novels = Class(Books)
   private
      author: String;
   
   public
      constructor Create(t: String); overload;
      constructor Create(a: String; t: String; p: real); overload;
      
      procedure setAuthor(a: String); // sets author for a book
      function getAuthor(): String; // retrieves author name
      
      procedure Display(); override;
end;
var
   n1, n2: Novels;

//default constructor 
constructor Books.Create(t : String; p: real);
begin
   title := t;
   price := p;
end;

procedure Books.setTitle(t : String); //sets title for a book
begin
   title := t;
end;

function Books.getTitle() : String; //retrieves title
begin
   getTitle := title;
end;

procedure Books.setPrice(p : real); //sets price for a book
begin
   price := p;
end;

function Books.getPrice() : real; //retrieves price
begin
   getPrice:= price;
end;

procedure Books.Display();
begin
   writeln('Title: ', title);
   writeln('Price: ', price);
end;

(* Now the derived class methods  *)
constructor Novels.Create(t: String);
begin
   inherited Create(t, 0.0);
   author:= ' ';
end;

constructor Novels.Create(a: String; t: String; p: real);
begin
   inherited Create(t, p);
   author:= a;
end;

procedure Novels.setAuthor(a : String); //sets author for a book
begin
   author := a;
end;

function Novels.getAuthor() : String; //retrieves author
begin
   getAuthor := author;
end;

procedure Novels.Display();
begin
   writeln('Title: ', title);
   writeln('Price: ', price:5:2);
   writeln('Author: ', author);
end;

begin 
   n1 := Novels.Create('Gone with the Wind');
   n2 := Novels.Create('Ayn Rand','Atlas Shrugged', 467.75);
   n1.setAuthor('Margaret Mitchell');
   n1.setPrice(375.99);
   n1.Display;
   n2.Display;
end.

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Title: Gone with the Wind
Price: 375.99
Author: Margaret Mitchell
Title: Atlas Shrugged
Price: 467.75
Author: Ayn Rand

Vale la pena señalar los siguientes puntos importantes:

  • Los miembros de la clase Libros tienen protected visibilidad.

  • La clase Novels tiene dos constructores, por lo que overload El operador se utiliza para la sobrecarga de funciones.

  • El procedimiento Books.Display ha sido declarado virtual, de modo que el mismo método de la clase Novels pueda override eso.

  • El constructor Novels.Create llama al constructor de la clase base usando el inherited palabra clave.

Interfaces

Las interfaces se definen para proporcionar un nombre de función común a los implementadores. Diferentes implementadores pueden implementar esas interfaces de acuerdo con sus requisitos. Se puede decir que las interfaces son esqueletos implementados por desarrolladores. A continuación se muestra un ejemplo de interfaz:

type  
   Mail = Interface  
      Procedure SendMail;  
      Procedure GetMail;  
   end;  
   
   Report = Class(TInterfacedObject,  Mail)  
      Procedure SendMail;  
      Procedure GetMail;  
   end;

Tenga en cuenta que, cuando una clase implementa una interfaz, debe implementar todos los métodos de la interfaz. Si no se implementa un método de interfaz, el compilador dará un error.

Clases abstractas

Una clase abstracta es aquella que no se puede instanciar, solo heredar. Una clase abstracta se especifica al incluir la palabra símbolo abstracto en la definición de clase, así:

type
   Shape = ABSTRACT CLASS (Root)
      Procedure draw; ABSTRACT;
      ...
   end;

Cuando se hereda de una clase abstracta, todos los métodos marcados como abstractos en la declaración de clase del padre deben ser definidos por el hijo; además, estos métodos deben definirse con la misma visibilidad.

Palabra clave estática

Declarar miembros de clase o métodos como estáticos los hace accesibles sin necesidad de instanciar la clase. No se puede acceder a un miembro declarado como estático con un objeto de clase instanciado (aunque un método estático sí). El siguiente ejemplo ilustra el concepto:

program StaticExample;
{$mode objfpc}
{$static on}
type
   myclass=class
      num : integer;static;
   end;
var
   n1, n2 : myclass;
begin
   n1:= myclass.create;
   n2:= myclass.create;
   n1.num := 12;
   writeln(n2.num);
   n2.num := 31;
   writeln(n1.num);
   writeln(myclass.num);
   myclass.num := myclass.num + 20;
   writeln(n1.num);
   writeln(n2.num);
end.

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

12
31
31
51
51

Debe usar la directiva {$ static on} para usar los miembros estáticos.