Delphi. Урок 19. Поля-объекты внутри классов
В этом уроке мы рассмотрим особенности создания объектов в качестве полей классов.
Как уже упоминалось в предыдущем уроке, полями могут являться не только обыкновенные переменные типа Integer, String или Boolean, но также и любые экземпляры других классов. Забегая немного вперед, скажу, что и это далеко не все, что можно объявить внутри класса. Предположим, у нас существует три класса, описанные следующим образом:
type
TTeacher = class
public
FullName: string;
constructor Create(NewFullName: string);
destructor Destroy;
end;
TStudent = class
public
FullName: string;
MathTeacher: TTeacher;
PhysicsTeacher: TTeacher;
BiologyTeacher: TTeacher;
constructor Create(NewFullName: string);
destructor Destroy;
end;
TSchool = class
public
SchoolNumber: integer;
Teachers: array [1..10] of TTeacher;
Students: array [1..100] of TStudent;
constructor Create(NewSchoolNumber: integer);
destructor Destroy;
end;
Внимательно рассмотрим описания этих классов. Реализации конструктора и деструктора принципиально ничем не отличаются от тех, что были представлены в предыдущем уроке.
Как вы видите, мы создали три класса: учитель, ученик и школа. Класс TTeacher наиболее простой из приведенных, т.к. содержит лишь одно поле — полное имя. Класс TStudent как раз и демонстрирует нам пример создания поля, указывающее на другой объект другого класса. Кстати говоря, поля могут ссылаться и на другой объект этого же класса.
[warning]Чтобы создать внутри класса А поле, которое будет являться экземпляром другого класса B, необходимо учесть, что класс B должен быть описан ДО описания класса A.[/warning]
Таким образом, класс нашего ученика содержит в себе три поля — ссылки на преподавателей математики, физики и биологии, а именно на экземпляры класса TTeacher.
Присваивать этим полям значения можно достаточно легко, например так:
var Teacher1: TTeacher;
Student1: TStudent;
begin
Teacher1 := TTeacher.Create('Tyapkin Vladimir Alexeevich');
Student1 :- TStudent.Create('Pupkin Vasilyi Vasilievich');
Student1.MathTeacher := Teacher1;
end;
[important]При присвоении одному объекту А значения другого объекта B, для A присваивается лишь указатель на объект B. Это означает, что и A и B будут ссылаться на один и тот же объект, на одну и ту же область оперативной памяти. Например, при изменении впоследствии объекта B (или А), изменится и значение, возвращаемое объектом А (или B), т.к. они являются одним и тем же объектом. Эта особенность очень широко используется, т.к. позволяет легко создавать различные взаимосвязи между классами. В данном случае все экземпляры классов можно считать указателями, если у одного объекта имеется несколько экземпляров, то они не копируют данные, они указывают на одни и те же данные.[/important]
Нельзя пройти мимо очень полезной функции Assigned, которая позволяет проверить, был ли присвоен какой-либо объект данному полю:
var Teacher1: TTeacher;
Student1: TStudent;
begin
Teacher1 := TTeacher.Create('Tyapkin Vladimir Alexeevich');
Student1 :- TStudent.Create('Pupkin Vasilyi Vasilievich');
...
if not Assigned(Student1.MathTeacher) then
Student1.MathTeacher := Teacher1
else
ShowMessage('Студенту '+Student1.FullName + ' уже присвоен учитель математики '+Student1.MathTeacher);
end;
Ну и рассмотрим еще один несложный пример, в котором мы уже будем работать с целым массивом этих самых указателей на объекты. Для этого изучим класс TSchool. В нем описаны два массива: учителя и ученики. Заполнять их можно также просто:
var i, n, j: integer;
school: TSchool;
begin
school := TSchool.Create(123);
n := 1;
for i := 1 to 10 do begin
school.Teachers[i] := TTeacher.Create('Teacher ' + IntToStr(i));
for j := 1 to 10 do begin
school.Students[n] := TStudent.Create('Student '+ IntToStr(n));
school.Students[n].MathTeacher := school.Teachers[i];
inc(n);
end;
end;
end;
В данном примере мы создаем 10 учителей и 100 учеников. Каждому ученику присваиваем в цикле учителя математики. Несложно заметить, что присвоение здесь выглядит также как и в случае с обыкновенными переменными, но нужно помнить, что присваиваются не значения самих объектов, а присваиваются указатели на объект.
Чтобы очистить поле или переменную-экземпляр, в котором ранее содержался объект, достаточно ему присвоить значение nil:
var school, schoollink: TSchool;
begin
school := TSchool.Create;
schoollink := school; // Создаем еще один указатель на объект
schoollink := nil; // Schoollink больше не является указателем на school
end;
[warning]Если вы обнулите (присвоите nil) всем указателем на один объект, то вы потеряете возможность обращаться к содержимому этого экземпляра. А как следствие это будет являться причиной утечки памяти, т.к. объект теперь никак не сможет быть освобожден, если ранее не был вызван деструктор.[/warning]
Ну и, конечно же, мы всегда можем обращаться к полям-объектам внутри методов текущего класса, а соответственно реализовывать необходимые аналогичные присвоения и многое другое.
-
Илья
-
Cyberexpert
-