Правила наследования конструкторов рассматриваются здесь.
Следует обратить внимание, что конструктор базового класса вызывается в этом случае как процедура, а не как функция, при этом создания нового объекта не происходит.
Если в методе вызывается метод базового класса с теми же параметрами, то можно использовать запись inherited, не указывая имя метода и параметры. Например, метод Student.Print можно записать таким образом:
procedure Print;
begin
inherited;
writeln('Курс: ',course,' Группа: ',group);
end;
Наследование конструкторов
Правила наследования конструкторов - достаточно сложные. В разных языках программирования приняты разные решения на этот счет. В частности, в Delphi Object Pascal все конструкторы наследуются. В .NET, напротив, конструкторы не наследуются. Причина такого решения - каждый класс сам должен отвечать за инициализацию своих экземпляров. Единственное исключение в .NET - если класс вовсе не определяет конструкторов, то автоматически генерируется конструктор без параметров, называемый конструктором по умолчанию.
В PascalABC.NET принято промежуточное решение. Если класс не определяет конструкторов, то все конструкторы предка автоматически генерируются в потомке, вызывая соответствующие конструкторы предка (можно также говорить, что они наследуются). Если в классе определяются конструкторы, то конструкторы предка не генерируются. Конструктор по умолчанию, если он явно не определен, генерируется автоматически в любом случае и является protected.
Кроме того, в .NET обязательно в конструкторе потомка первым оператором должен быть вызван конструктор предка; в Object Pascal это необязательно. Если в PascalABC.NET конструктор предка вызывается из конструктора потомка, то этот вызов должен быть первым оператором. Если конструктор предка явно не вызывается из конструктора потомка, то неявно первым оператором в конструкторе потомка вызывается конструктор предка по умолчанию (т.е. без параметров). Если такого конструктора у предка нет (это может быть класс, откомпилированный другим .NET-компилятором или входящий в стандартную библиотеку классов - все классы, откомпилированные PascalABC.NET, имеют конструктор по умолчанию), то возникает ошибка компиляции.
Например:
type
A = class
i: integer;
// конструктор по умолчанию не определен явно, поэтому генерируется автоматически
constructor Create(i: integer);
begin
Self.i := i;
end;
end;
B = class(A)
j: integer;
constructor Create;
begin
// конструктор по умолчанию базового класса вызывается автоматически
// конструктор по умолчанию определен явно, поэтому не генерируется автоматически
j := 1;
end;
constructor Create(i,j: integer);
begin
inherited Create(i);
Self.j := j;
end;
end;
C = class(B)
// класс не определяет конструкторов, поэтому
// конструктор по умолчанию и constructor Create(i,j: integer)
// генерируются автоматически, вызывая в своем теле соответствующие конструкторы предка
end;
Виртуальные методы и полиморфизм
Полиморфизм (от греч. много форм) - это свойство классов, связанных наследованием, иметь различную реализацию входящих в них методов, и способность переменной базового класса вызывать методы того класса, объект которого содержится в этой переменной в момент вызова метода.
Полиморфизм используется в ситуации, когда для группы взаимосвязанных объектов требуется выполнить единое действие, но каждый из этих объектов должен выполнить указанное действие по-своему (т.е. у действия возникает много форм). Для этого определяется базовый для всех объектов класс с виртуальными методами, предусмотренными для меняющегося поведения, после чего эти методы переопределяется в потомках.
Для пояснения рассмотрим переопределение метода в подклассе:
type
Base = class
public
procedure Print;
begin
writeln('Base');
end;
end;
Derived = class(Base)
public
procedure Print;
begin
writeln('Derived');
end;
end;
Присвоим переменной базового класса Base объект производного класса Derived и вызовем метод Print.
var b: Base := new Derived;
b.Print;
Какая версия метода Print вызывается - класса Base или класса Derived? В данном случае решение будет принято еще на этапе компиляции: вызовется метод Print класса Base, заявленного при описании переменной b. Говорят, что имеет место раннее связывание имени метода с его телом. Если же решение о том, какой метод вызывать, принимается на этапе выполнения программы в зависимости от реального типа объекта, на который ссылается переменная b, то в данном случае вызывается метод Derived.Print (говорят также, что имеет место позднее связывание). Методы, для которых реализуется позднее связывание, называются виртуальными, а переменная базового класса, через которую осуществляется вызов виртуального метода, - полиморфной переменной. Таким образом, полиморфизм реализуется вызовом виртуальных функций через переменную базового класса. Тип класса, который хранится в данной переменной на этапе выполнения, называется динамическим типом этой переменной.
Для того чтобы сделать метод виртуальным, следует в объявлении этого метода после заголовка указать ключевое слово virtual с последующей ;. Для переопределения виртуального метода следует использовать ключевое слово override:
type
Base = class
public
procedure Print; virtual;
begin
writeln('Base');
end;
end;
Derived = class(Base)
public
procedure Print; override;
begin
writeln('Derived');
end;
end;
Теперь в аналогичном участке кода.
var b: Base := new Derived;
b.Print;
вызывается метод Print класса Derived за счет того что решение о вызове метода откладывается на этап выполнения программы.
Говорят, что методы Print завязаны в цепочку виртуальности. Чтобы разорвать ее (не вызывать методы в подклассах виртуально) используется ключевое слово reintroduce:
type
DerivedTwice1 = class(Derived)
public
procedure Print; reintroduce;
begin
writeln('DerivedTwice1');
end;
end;
Если мы хотим начать новую цепочку виртуальности, то следует использовать и virtual и reintroduce: