Паскаль - Классы

Вы видели, что объекты Pascal демонстрируют некоторые характеристики объектно-ориентированной парадигмы. Они реализуют инкапсуляцию, скрытие данных и наследование, но также имеют ограничения. Например, объекты Паскаля не участвуют в полиморфизме. Таким образом, классы широко используются для реализации правильного объектно-ориентированного поведения в программе, особенно в программном обеспечении на основе графического интерфейса.

Класс определяется почти так же, как объект, но является указателем на объект, а не на сам объект. Технически это означает, что класс выделяется в куче программы, а объект - в стеке. Другими словами, когда вы объявляете переменную типа объекта, она будет занимать столько места в стеке, сколько размер объекта, но когда вы объявляете переменную типа класса, она всегда будет иметь размер указателя в стеке. Фактические данные класса будут в куче.

Определение классов Паскаля

Класс объявляется так же, как объект, с использованием объявления типа. Общая форма объявления класса следующая:

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;

Стоит отметить следующие важные моменты -

  • Определения классов должны входить только в часть программы с объявлением типа.

  • Класс определяется с помощью class ключевое слово.

  • Поля - это элементы данных, которые существуют в каждом экземпляре класса.

  • Методы объявляются в определении класса.

  • Есть предопределенный конструктор под названием Createв классе Root. Каждый абстрактный класс и каждый конкретный класс является потомком Root, поэтому все классы имеют по крайней мере один конструктор.

  • Существует предопределенный деструктор под названием Destroyв классе Root. Каждый абстрактный класс и каждый конкретный класс является потомком Root, поэтому все классы имеют хотя бы один деструктор.

Давайте определим класс Rectangle, который имеет два члена данных целочисленного типа - длину и ширину, а также некоторые функции-члены для управления этими членами данных и процедуру для рисования прямоугольника.

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;

Давайте напишем полную программу, которая создаст экземпляр класса прямоугольника и нарисует прямоугольник. Это тот же пример, который мы использовали при обсуждении объектов Паскаля. Вы обнаружите, что обе программы почти одинаковы, за следующими исключениями:

  • Вам нужно будет включить директиву {$ mode objfpc} для использования классов.

  • Вам потребуется включить директиву {$ m +} для использования конструкторов.

  • Создание экземпляра класса отличается от создания экземпляра объекта. Только объявление переменной не создает места для экземпляра, вы будете использовать конструктор create для выделения памяти.

Вот полный пример -

{$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.

Когда приведенный выше код компилируется и выполняется, он дает следующий результат:

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

Видимость членов класса

Видимость указывает на доступность учащихся. Члены класса Pascal имеют пять типов видимости -

Старший Нет Видимость и доступность
1

Public

Эти участники всегда доступны.

2

Private

К этим членам можно получить доступ только в модуле или модуле, который содержит определение класса. К ним можно получить доступ изнутри методов класса или извне.

3

Strict Private

Доступ к этим членам можно получить только из методов самого класса. Другие классы или дочерние классы в том же модуле не могут получить к ним доступ.

4

Protected

Это то же самое, что и private, за исключением того, что эти члены доступны для потомков, даже если они реализованы в других модулях.

5

Published

Это то же самое, что и Public, но компилятор генерирует информацию о типе, которая необходима для автоматической потоковой передачи этих классов, если компилятор находится в состоянии {$ M +}. Поля, определенные в опубликованном разделе, должны относиться к типу класса.

Конструкторы и деструкторы для классов Паскаля

Конструкторы - это специальные методы, которые вызываются автоматически при создании объекта. Таким образом, мы в полной мере пользуемся этим поведением, инициализируя многие вещи с помощью функций-конструкторов.

Паскаль предоставляет специальную функцию create () для определения конструктора. Вы можете передать в функцию-конструктор любое количество аргументов.

В следующем примере будет создан один конструктор для класса с именем Books, который инициализирует цену и название книги во время создания объекта.

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.

Когда приведенный выше код компилируется и выполняется, он дает следующий результат:

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

Как и неявный конструктор с именем create, существует также неявный метод деструктора destroy, с помощью которого вы можете освободить все ресурсы, используемые в классе.

Наследование

Определения классов Pascal могут необязательно наследоваться от определения родительского класса. Синтаксис следующий -

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

В следующем примере представлен класс романов, который наследует класс Books и добавляет дополнительные функции в зависимости от требований.

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.

Когда приведенный выше код компилируется и выполняется, он дает следующий результат:

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

Стоит отметить следующие важные моменты -

  • Члены класса "Книги" имеют protected видимость.

  • Класс Novels имеет два конструктора, поэтому overload Оператор используется для перегрузки функции.

  • Заявлена ​​процедура отображения книг. virtual, так что тот же метод из класса Novels может override Это.

  • Конструктор Novels.Create вызывает конструктор базового класса с помощью inherited ключевое слово.

Интерфейсы

Интерфейсы определены для предоставления разработчикам общего имени функции. Различные разработчики могут реализовать эти интерфейсы в соответствии со своими требованиями. Можно сказать, интерфейсы - это скелеты, которые реализуют разработчики. Ниже приведен пример интерфейса -

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

Обратите внимание: когда класс реализует интерфейс, он должен реализовывать все методы интерфейса. Если метод интерфейса не реализован, компилятор выдаст ошибку.

Абстрактные классы

Абстрактный класс - это класс, который нельзя создать, а только унаследовать. Абстрактный класс указывается включением абстрактного символа слова в определение класса, например:

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

При наследовании от абстрактного класса все методы, отмеченные как абстрактные в объявлении родительского класса, должны быть определены потомком; кроме того, эти методы должны быть определены с одинаковой видимостью.

Статическое ключевое слово

Объявление членов или методов класса статическими делает их доступными без необходимости создания экземпляра класса. К члену, объявленному как статический, нельзя получить доступ с помощью созданного объекта класса (хотя статический метод может). Следующий пример иллюстрирует концепцию -

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.

Когда приведенный выше код компилируется и выполняется, он дает следующий результат:

12
31
31
51
51

Вы должны использовать директиву {$ static on} для использования статических членов.