Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ООП.doc
Скачиваний:
23
Добавлен:
08.11.2018
Размер:
1.4 Mб
Скачать
  1. Указатели на класс

Вся информация, описывающая класс и представляющая собой таблицы виртуальных и динамических методов, создается и размещается в памяти еще на этапе компиляции. Доступ к ней можно получить, не создавая экземпляр класса. Для этого следует описать соответствующий указатель, который называется указателем на класс, или указателем на объектный тип (class reference). Он описывается при помощи зарезервированных слов class of. Например, указатель на TObject описан в модуле SYSTEM.PAS и называется TClass:

Type TClass = class of TObject;

Константа типа указатель на класс – это имя класса. Переменной типа указатель на класс можно присвоить значение константы соответствующего типа или другой переменной. Указатели на классы тоже подчиняются правилам приведения объектных типов. Указатель на класс-предок может ссылаться на любые дочерние классы. Обратное невозможно. Например:

Interface Type TObject1 = class ... end;

TObject2 = class(TObject1) ... end;

TObject1Class = class of TObject1; TObject2Class = class of TObject2;

Implementation Procedure Test; Var Cl1: TObject1Class; Cl2: TObject2Class; Begin Cl1 := TObject2; //допустимо //Cl2 := TObject1; //недопустимо End;

Указатели на класс предоставляют возможность рассматривать класс как некий объект, с которым можно работать. Тип «указатель на класс» в этом случае может рассматриваться как так называемый метакласс. Указатели на класс полезны, когда необходимо использовать методы класса или виртуальные конструкторы.

Виртуальные конструкторы

Рассмотрим сначала использование виртуальных конструкторов. Как уже было сказано выше, конструкторы, как и обычные методы, можно объявлять, используя директивы virtual, dynamic, override. Такие конструкторы называют виртуальными. Виртуальные конструкторы используются, если решение о том, объект какого класса будет создан, принимается во время исполнения программы.

Как известно, объект создается следующим образом:

<Имя объекта> := <Имя класса>.Create(<Параметры конструктора>);

Имя класса в этой конструкции может быть заменено именем переменной типа указатель на класс. Причем указатель на класс-предок может указывать на любой из классов-потомков. Конструктор класса-предка должен быть объявлен как virtual или dynamic, а конструкторы потомков перекрывать его с использованием директивы override. В этом случае в результате будет создан объект класса, соответствующего реальному значению указателя на класс. При вызове конструктора класса через имя класса спецификатор virtual будет проигнорирован, и конструктор будет вызываться как статический.

Таким образом, использование виртуальных конструкторов позволяет создавать объекты, чей фактический тип неизвестен во время компиляции.

Для примера возьмем уже рассматривавшуюся ранее иерархию классов данных о разных категориях лиц:

TPerson = class //имеет свойства имя и фамилия ... public //конструктор constructor Create; virtual; //свойство фамимлия property Surname: string read FSurname write SetSurname; //свойство имя property Name: string read FName write SetName; ... end;

TStudent = class(TPerson) //добавляется свойство код группы ... public constructor Create; override; //свойство код группы property Group: string read FGroup write SetGroup; ... end;

TEmployee = class(TPerson) //добавляется свойство табельный номер ... public constructor Create; override; //свойство табельный номер property TabNum: string read FTabNum write SetTabNum; ... end;

Эти классы имеют виртуальные конструкторы, т.е. конструктор предка TPerson объявлен virtual, а конструкторы потомков перекрывают его с использованием директивы override.

Тип указатель на класс, который может ссылаться на все классы данной иерархии, описывается следующим образом:

type TPersonClass = class of TPerson;

Разработаем функцию создания объекта, класс которого задается во время выполнения программы:

function CreatePerson(PersonClass: TPersonClass; const Name: string; const Surname: string): TPerson; begin Result := PersonClass.Create; with Result do begin Result.Name := Name; Result.Surname := Surname; end; end;

Функция CreatePerson требует параметра типа указатель на класс, чтобы определить, объект какого именно класса создавать. Она использует этот параметр для вызова конструктора соответствующего класса. Так как переменной типа указатель на класс может быть присвоен идентификатор класса, то вызов CreatePerson может, например, выглядеть следующим образом:

Employee := CreatePerson(TEmployee, 'Иванов', ‘Петр’) as TEmployee;

или

for i := 1 to n do begin GetData(isStudent, Name, Surname);//вводятся данные //создаваемого объекта //isStudent – признак того, студент это или рабочий //Name, Surname – фамилия, имя if (isStudent) then Persons[i] := CreatePerson(TStudent, Name, Surname) else Persons[i] := CreatePerson(TEmployee, Name, Surname);

В этом примере объекты разных классов создаются и инициализируются однотипно, путем вызова функции CreatePerson. В качестве параметра ей передается имя класса, объект которого надо создать. Это может быть любой класс, производный от TPerson. Возвращает эта функция указатель на созданный объект. Этот указатель имеет тип объект класса-предка TPerson, но, при необходимости может быть приведен к реальному типу. Таким образом, этот пример показывает, как механизм виртуальных конструкторов и указателей на класс позволяет создавать объекты, чей тип определяется не на этапе компиляции, а при исполнении программы.