Delphi. Урок 19. Поля-объекты внутри классов

Уроки для начинающих   22 октября 2013  Автор статьи: admin 
geekbrains.ru/

В этом уроке мы рассмотрим особенности создания объектов в качестве полей классов.
Как уже упоминалось в предыдущем уроке, полями могут являться не только обыкновенные переменные типа 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]
Ну и, конечно же, мы всегда можем обращаться к полям-объектам внутри методов текущего класса, а соответственно реализовывать необходимые аналогичные присвоения и многое другое.

  • Илья

    А что мне делать если я плохо запоминаю всё это?

    • Больше практиковаться!

Научиться программировать

  • на Delphi

  • на Java

  • на C++

geekbrains.ru/
geekbrains.ru/