Delphi 7. Занятие 2_7. Часть 3.
Цели занятия:
- механизм наследования при создании классов;
- конструктор класса — создание экземпляра объекта;
- унаследованный конструктор; inherited;
- конструктор с параметрами.
Как отмечалось ранее, экземпляры объектов создаются с помощью статического метода класса «create». Далее рассмотрим различные варианты применения этого метода.
Delphi создание экземпляра класса
Определим два новых класса.
Первый класс наследуем от TObject. Добавляем поле ii:integer;.
TYPE
TMyClassA=class(TObject) //если наследование идёт от TObject, то можно просто записать TMyClassA=class
ii:integer;
end;
Второй класс наследуем от первого и добавляем поле ss:string;
TMyClassB=class(TMyClassA)
ss:string;
end;
В обработчике клика по кнопке создаём экземпляры объектов этих классов и выводим интересующие нас значения:
procedure TForm1.Button1Click(Sender: TObject);
var vA:TMyClassA; vB:TMyClassB;
begin
vA:=TMyClassA.Create;
vB:=TMyClassB.Create;
vA.ii:=10;
vB.ss:=’BBBBB’;
vB.ii:=20;
form1.Edit1.Text:=intToStr(vA.ii);
form1.Edit2.Text:=intToStr(vB.ii);
form1.Edit3.Text:=vB.ss;
form1.Edit4.Text:=TMyClassA.ClassName;
form1.Edit5.Text:=TMyClassB.ClassName;
form1.Edit6.Text:=vA.ClassName;
form1.Edit7.Text:=vB.ClassName;
end;
В результате будем иметь:

В поле 1) отображено значение поля ii объекта vA.
В объект vB введено поле ss, а также унаследовано поле ii. Значение поля ii объекта vB отображено в поле 2).
В поле 3) отображено значение поля ss объекта vB.
В полях 4,5,6 и 7 отображено значение, возвращаемое функцией класса ClassName, вызываемой как непосредственно ссылкой из класса (значения 4 и 5), как и через экземпляр класса (значения 6 и 7).
В приведённом примере при создании экземпляра объекта vA вызывался конструктор из класса-родителя TObject.
Для класса TMyClassB родителем является класс TMyClassA. Так как в классе TMyClassA конструктор явно не задан, то берётся конструктор от его родителя — класса TObject.
Delphi конструктор класса
Пусть теперь в первом классе, порожденном от TObject, задан свой конструктор create.
TYPE
TMyClassC=class(TObject)
kk:integer;
constructor create;
end;
TMyClassD=class(TMyClassC)
st:string;
end;
Реализация тела конструктора:
constructor TMyClassC.create;
begin
kk:=30; //задаём начальное значение поля;
end;
procedure TForm1.Button2Click(Sender: TObject);
var vC:TMyClassC; vD:TMyClassD;
begin
vC:=TMyClassC.create;
vD:=TMyClassD.create;
vD.st:=’DDDD’;
form1.Edit8.Text:=intToStr(vC.kk);
form1.Edit9.Text:=intToStr(vD.kk);
form1.Edit10.Text:=vD.st;
end;
Как видно из кода, после создания экземпляра объекта vC его полю kk значение явно не присваивается.
Явно присваиваются значения полям kk и st объекта vD.
Вызов конструктора для класса TMyClassD.create влечёт за собой вызов конструктора класса-родителя TMyClassC:
constructor TMyClassC.create;
begin
kk:=30; //задаём начальное значение поля;
end;
Прежде, чем начнёт выполняться тело конструктора TMyClassC.create, будет вызван конструктор класса-родителя (то есть конструктор из TObject) и будет создан объект vD.
После создания облъекта явно заданные конструкторы начнут просматриваться в обратном порядке и в них будет выполнен код, содержащийся в их описаниях.
В данном случае выполнится инициализация поля kk:=30;
Это видно из приведенного примера:

Хотя полю vD.kk значение явно не задаётся, однако его определяет конструктор класса-родителя.
Если добавить оператор:
vD.kk:=40;
то результат будет следующим:

Теперь создадим оба класса с конструкторами.
TYPE
TMyClassG=class(TObject)
nn:integer;
constructor create;
end;
TMyClassH=class(TMyClassG)
st:string;
constructor create;
end;
constructor TMyClassG.create;
begin
nn:=60;
end;
constructor TMyClassH.create;
begin
nn:=70;
st:=’HHHH’;
end;
И создание объектов:
procedure TForm1.Button4Click(Sender: TObject);
var vG:TMyClassG; vH:TMyClassH;
begin
vG:=TMyClassG.create;
vH:=TMyClassH.create;
form1.Edit14.Text:=intToStr(vG.nn);
form1.Edit15.Text:=intToStr(vH.nn);
form1.Edit16.Text:=vH.st;
vG.nn:=80;
vH.nn:=90;
vH.st:=’SSSS’;
form1.Edit17.Text:=intToStr(vG.nn);
form1.Edit18.Text:=intToStr(vH.nn);
form1.Edit19.Text:=vH.st;
end;
Результат:

Поля 1, 2 и 3 сформированы конструктором. Поля 4, 5, и 6 отображают значения, заданные непосредственно для экземпляров объектов.
Теперь рассмотрим ситуацию:
TYPE
TMyClassM=class(TObject)
nn:integer;
constructor create;
end;
TMyClassN=class(TMyClassM)
st:string;
constructor create;
end;
constructor TMyClassM.create;
begin
nn:=60;
end;
constructor TMyClassN.create;
begin
st:=’NNNN’;
end;
procedure TForm1.Button5Click(Sender: TObject);
var vM:TMyClassM; vN:TMyClassN;
begin
vM:=TMyClassM.create;
vN:=TMyClassN.create;
form1.Edit20.Text:=intToStr(vM.nn);
form1.Edit21.Text:=intToStr(vN.nn);
form1.Edit22.Text:=vN.st;
end;
Оба класса имеют конструктор. Но конструктор класса TMyClassM определяет значение поля: nn:=60;. А в конструкторе TMyClassN этого поля нет. Тогда будем иметь следующий результат:

В конструкторе
constructor TMyClassN.create;
begin
st:=’NNNN’;
end;
поле nn не инициализируется, и начальное значение не передаётся из конструктора родительского класса.
Delphi inherited
Чтобы исправить ситуацию, надо использовать конструктор класса delphi, унаследованный от класса-родителя.
Для этого необходимо использовать ключевое слово «inherited» (унаследованный):
constructor TMyClassN.create;
begin
inherited create; // вызываем конструктор, унаследованный от TMyClassM;
st:=’NNNN’;
end;
В результате будем иметь:

Наконец, рассмотрим конструктор с параметрами.
TYPE
TMyClassP=class(TObject)
nn:integer;
constructor create(n:integer=5);
end;
TMyClassQ=class(TMyClassP)
st:string;
constructor create(s:string;nm:integer=88);
end;
constructor TmyClassP.create(n:integer=5); //параметр имеет значение по умолчанию
begin
nn:=n;
end;
constructor TMyClassQ.create(s:string;nm:integer=88); //параметр имеет значение по умолчанию
begin
inherited create(nm);
st:=s;
end;
procedure TForm1.Button6Click(Sender: TObject);
var vP:TMyClassP; vQ:TMyClassQ;
begin
vP:=TMyClassP.create(15);
vQ:=TMyClassQ.create(‘PPPPP’,25);
form1.Edit23.Text:=intToStr(vP.nn);
form1.Edit24.Text:=intToStr(vQ.nn);
form1.Edit25.Text:=vQ.st;
end;
В результате имеем:

Так как параметры конcтруктора имеют значения по умолчанию, организуем вызов конструкторов в следующем виде:
vP:=TMyClassP.create();
vQ:=TMyClassQ.create(‘PPPPP’,25);
vP:=TMyClassP.create(15);
vQ:=TMyClassQ.create(‘PPPPP’);
и
vP:=TMyClassP.create();
vQ:=TMyClassQ.create(‘PPPPP’);
Тогда, соответственно, будем иметь:
Значения 5 и 88 — значения по умолчанию.


