Вы видели, что объекты Pascal обладают некоторыми характеристиками объектно-ориентированной парадигмы. Они реализуют инкапсуляцию, сокрытие данных и наследование, но у них также есть ограничения. Например, объекты 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, поэтому у всех классов есть хотя бы один конструктор.
-
В классе Root есть предопределенный деструктор Destroy . Каждый абстрактный класс и каждый конкретный класс является потомком Root, поэтому у всех классов есть по крайней мере один деструктор.
Определения классов должны входить только в часть описания типа программы.
Класс определяется с помощью ключевого слова class .
Поля — это элементы данных, которые существуют в каждом экземпляре класса.
Методы объявляются в определении класса.
Существует предопределенный конструктор с именем Create в классе Root. Каждый абстрактный класс и каждый конкретный класс является потомком Root, поэтому у всех классов есть хотя бы один конструктор.
В классе Root есть предопределенный деструктор Destroy . Каждый абстрактный класс и каждый конкретный класс является потомком 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;
Давайте напишем полную программу, которая создаст экземпляр класса прямоугольника и нарисует прямоугольник. Это тот же пример, который мы использовали при обсуждении объектов Pascal. Вы найдете, что обе программы почти одинаковы, за следующими исключениями —
-
Вам нужно будет включить директиву {$ mode objfpc} для использования классов.
-
Вам нужно будет включить директиву {$ m +} для использования конструкторов.
-
Создание экземпляров класса отличается от создания объектов. Только объявление переменной не создает пространство для экземпляра, вы будете использовать конструктор create для выделения памяти.
Вам нужно будет включить директиву {$ 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 * * * * * * * * * * * * * * * * * * * * * * * *
Видимость участников класса
Видимость указывает на доступность членов класса. Члены класса Паскаль имеют пять видов видимости —
Sr.No | Видимость и доступность |
---|---|
1 |
общественного Эти участники всегда доступны. |
2 |
Частный Эти члены могут быть доступны только в модуле или модуле, который содержит определение класса. К ним можно получить доступ из методов класса или извне. |
3 |
Строгий Частный Эти члены могут быть доступны только из методов самого класса. Другие классы или дочерние классы в том же модуле не могут получить к ним доступ. |
4 |
защищенный Это то же самое, что и private, за исключением того, что эти члены доступны для потомков, даже если они реализованы в других модулях. |
5 |
опубликованный Это то же самое, что и Public, но компилятор генерирует информацию о типе, которая необходима для автоматической потоковой передачи этих классов, если компилятор находится в состоянии {$ M +}. Поля, определенные в опубликованном разделе, должны иметь тип класса. |
общественного
Эти участники всегда доступны.
Частный
Эти члены могут быть доступны только в модуле или модуле, который содержит определение класса. К ним можно получить доступ из методов класса или извне.
Строгий Частный
Эти члены могут быть доступны только из методов самого класса. Другие классы или дочерние классы в том же модуле не могут получить к ним доступ.
защищенный
Это то же самое, что и private, за исключением того, что эти члены доступны для потомков, даже если они реализованы в других модулях.
опубликованный
Это то же самое, что и 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;
В следующем примере представлен класс Novel, который наследует класс 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
Стоит отметить следующие важные моменты —
-
Члены класса Книги имеют защищенную видимость.
-
Класс Novels имеет два конструктора, поэтому для перегрузки функций используется оператор перегрузки.
-
Процедура Books.Display объявлена виртуальной , поэтому тот же метод из класса Novels может переопределить ее.
-
Конструктор Novels.Create вызывает конструктор базового класса с использованием унаследованного ключевого слова.
Члены класса Книги имеют защищенную видимость.
Класс Novels имеет два конструктора, поэтому для перегрузки функций используется оператор перегрузки.
Процедура Books.Display объявлена виртуальной , поэтому тот же метод из класса Novels может переопределить ее.
Конструктор Novels.Create вызывает конструктор базового класса с использованием унаследованного ключевого слова.
Интерфейсы
Интерфейсы определены для обеспечения общего имени функции для разработчиков. Различные исполнители могут реализовать эти интерфейсы в соответствии со своими требованиями. Можно сказать, что интерфейсы — это скелеты, которые реализуются разработчиками. Ниже приведен пример интерфейса —
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} для использования статических членов.