Параметр self

Вернёмся к рассмотрению класса TPerson. Этот класс содержит два поля и два метода. Представим, что имеется 100 объектов класса TPerson. Так как каждый объект конкретизируется значениями своих полей, то в памяти должно содержаться 100 наборов полей класса. Означает ли это, что и код методов класса будет продублирован 100 раз? Определённо, нет. Код методов класса содержится в памяти в единственном числе, как и код любой подпрограммы. Однако как метод определяет, с полями какого объекта он работает?

var A, B: TPerson;

...

A.SetAge(10); // SetAge работает с A.fAge

B.SetAge(40); // SetAge работает с B.fAge

Для выявления конкретного объекта, с которым происходит работа, любому методу передаётся скрытый параметр self. Этот параметр указывает на объект, вызывающий метод. Тип параметра self совпадает с типом класса. Например, на уровне компилятора описание метода SetAge и работу с ним можно представить следующим образом:

procedure TPerson.SetAge(Age: Integer; self: TPerson);

begin

if Age > 0 then self.fAge:= Age

end;

...

TPerson.SetAge(10, A);

TPerson.SetAge(40, B);

Подчеркнём два важных момента:

1. Параметр self передаётся в любой метод;

2. Методы можно воспринимать как обыкновенные подпрограммы, которые принимают дополнительный параметр self.

Практически всегда в явном использовании self нет необходимости. Одно из исключений – использование одинаковых идентификаторов для полей класса и параметров метода. Предположим, что класс TPerson содержит поле с идентификатором Age, а не fAge. Тогда корректная реализация метода TPerson.SetAge должна выглядеть следующим образом:

procedure TPerson.SetAge(Age: Integer);

begin

// Age – параметр метода, self.Age – поле объекта

if Age > 0 then self.Age:= Age

end;

Следующий код показывает нетривиальный пример использования self. Перепишем метод GetWeight и деструктор Destroy из класса TBTree:

function TBTree.GetWeight;

begin

if self = nil then Result:= 0

else Result:= Weight + Left.GetWeight + Right.GetWeight

end;

destructor TBTree.Destroy;

begin

if self <> nil then begin

Left.Destroy;

Right.Destroy;

end

end;

В деструкторе TBTree.Destroy при помощи self проверяется, является ли объект инициализированным. Если объект инициализирован, вызываются деструкторы для левого и правого поддеревьев.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: