type
DerivedTwice2 = class(Derived)
public
procedure Print; virtual; reintroduce;
begin
writeln('DerivedTwice2');
end;
end;
Если переопределить виртуальную функцию невиртуальной без ключевого слова reintroduce, то ошибки не произойдет - будет выведено лишь предупреждение о том, что цепочка виртуальности нарушена. Таким образом, ключевое слово reintroduce в этой ситуации лишь подавляет вывод предупреждения.
При переопределении виртуального метода в подклассе его уровень доступа должен быть не ниже, чем в базовом классе. Например, public виртуальный метод не может быть переопределен в подклассе private-методом.
Абстрактные методы
Методы, предназначенные для переопределения в подклассах, объявляются с ключевым словом abstract и называются абстрактными. Данные методы являются виртуальными, но ключевое слово virtual использовать не нужно. Например:
type
Shape = class
private
x,y: integer;
public
constructor Create(xx,yy: integer);
begin
x := xx;
y := yy;
end;
procedure Draw; abstract;
end;
Классы, содержащие абстрактные методы, также называются абстрактными. Экземпляры этих классов создавать нельзя.
Классы с абстрактными методами используются как полуфабрикаты для создания других классов. Например:
type
Point = class(Shape)
public
procedure Draw; override;
begin
PitPixel(x,y,Color.Black);
end;
end;
Использование override при переопределении абстрактных методов обязательно, поскольку абстрактные методы являются разновидностью виртуальных.
Перегрузка операций
Перегрузка операций - это средство языка, позволяющее вводить операции над типами, определяемыми пользователем. В PascalABC.NET можно использовать только предопределенные значки операций. Перегрузка операций для типа T, являющегося классом или записью, осуществляется при помощи статической (классовой) функции-метода со специальным именем operator ЗнакОперации. Перегрузка специальных операций +=, -=, *=, /= осуществляется с помощью статической (классовой) процедуры-метода, первый параметр которой передается по ссылке.
Например:
type
Complex = record
re,im: real;
class function operator+(a,b: Complex): Complex;
begin
Result.re := a.re + b.re;
Result.im := a.im + b.im;
end;
class function operator=(a,b: Complex): boolean;
begin
Result := (a.re = b.re) and (a.im = b.im);
end;
end;
Для перегрузки операций действуют следующие правила:
* Перегружать можно все операции за исключением @ (взятие адреса), as, is, new. Кроме того, можно перегружать специальные бинарные операции +=, -=, *=, /=, не возвращающие значений.
* Перегружать можно только еще не перегруженные операции.
* Тип по крайней мере одного операнда должен совпадать с типом класса или записи, внутри которого определена операция.
* Перегрузка осуществляется с помощью статической функции-метода, количество параметров которой совпадает с количеством параметров соответствующей операции (2 - для бинарной, 1 - для унарной).
* Перегрузка операций +=, -=, *=, /= для соответствующих операторов осуществляется с помощью статической процедуры-метода, первый параметр которой передается по ссылке и имеет тип записи или класса, в котором определяется данная операция, второй - передается по значению и совместим по присваиванию с первым. Перегрузка остальных операций осуществляется с помощью статических функций-методов.
* Типы интерфейсов не могут быть типами параметров. Причина: типы параметров должны вычисляться на этапе компиляции.
* Операции приведения типа задаются статическими функциями, у которых вместо имени используется operator implicit (для неявного приведения типа) или operator explicit (для явного приведения типа).
Например:
type
Complex = record
...
class function operator implicit(d: real): Complex;
begin
Result.re := d;
Result.im := 0;
end;
class function operator explicit(c: Complex): string;
begin
Result := Format('({0},{1})',c.re,c.im);
end;
class procedure operator+=(var c: Complex; value: Complex);
begin
c.re += value.re;
c.im += value.im;
end;
class function operator+(c,c1: Complex): Complex;
begin
Result.re := c.re + c1.re;
Result.im := c.im + c1.im;
end;
end;
Можно перегружать операции с помощью методов расширения - в этом случае при описании подпрограммы не писать слово class. Например, так в системном модуле реализовано добавление числа к строке:
function string.operator+(str: string; n: integer): string;
begin
result := str + n.ToString;
end;
Классовые поля, методы и конструкторы
В классе можно объявить так называемые классовые (статические) поля и методы. Они не принадлежат конкретному экземпляру класса, а связаны с классом. Для их вызова используется точечная нотация, причем, перед точкой используется не имя объекта, а имя класса. Чтобы поле или метод сделать классовым (статическим), перед его именем следует указать ключевое слово class.
Например, определим для класса Person количество созданных объектов этого класса как статическое поле и организуем доступ к этому полю на чтение с помощью статической функции. После каждого вызова конструктора значение статического поля будет увеличиваться на 1:
type
Person = class
private
name: string;
age: integer;
class cnt: integer := 0;
public
constructor (n: string; a: integer);
begin
cnt += 1;
name := n;
age := a;
end;
class function Count: integer;
begin
Result := cnt;
end;
end;
begin
var p: Person := new Person('Иванов',20);
var p1: Person := new Person('Петров',18);