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.