.
Delphi Изнутри. Урок 7. Продолжаем изучать TObject (ClassParent, Pointer, self).
Автор megabax   
20.05.2010 г.
New Page 2

Delphi Изнутри. Урок 7. Продолжаем изучать TObject (ClassParent, Pointer, self).

Сегодня мы продвинемся в изучении TObject дальше и рассмотрим метод ClassParent. Если мы посмотрим объявление TObject, то увидим, что этот метод объявлен как классовый*:

Delphi Изнутри. Урок 7. Продолжаем изучать TObject (ClassParent, Pointer, self).

в чем, собственно говоря нет ничего удивительного, так как данная функция предназначена для определения родительского класса, а для этого нам не обязательно, что бы экземпляр класса хранился в памяти компьютера. Вот как реализован этот метод:

class function TObject.ClassParent: TClass;
{$IFDEF PUREPASCAL}
begin
    Pointer(Result) := PPointer(Integer(Self) + vmtParent)^;
    if Result <> nil then Pointer(Result) := PPointer(Result)^;
end;
{$ELSE}
asm
   MOV EAX,[EAX].vmtParent
   TEST EAX,EAX
   JE @@exit
   MOV EAX,[EAX]
   @@exit:
end;
{$ENDIF}

Как же работает данная функция? Сначала мы берем содержимое по адресу, смещенному на vmtParent от описания класса:

Pointer(Result) := PPointer(Integer(Self) + vmtParent)^;

Если это не nil, то мы берем содержимое ячейки, расположенной по этому адресу, а именно, ссылку на родительский класс. Если у нас класс не имеет родителя, например, тот же TObject, то метод вернет nil.

Где можно применить ClassParent? Например, для того, что бы проверить, является ли данный класс дочерним от заданного.  В качестве примера возьмем фрагмент кода библиотеки для создания компьютерных игр Easy Game Library:

function TEGBaseClass.IsChildOf(AName:String):Boolean;
var ClassRef: TClass;
begin
    ClassRef := ClassType;
    while ClassRef <> nil do
     begin
        if ClassRef.ClassName=AName then
        begin
            Result:=true;
            exit;
        end;
       ClassRef := ClassRef.ClassParent;
   end;
   Result:=false;
end;

Как работает данная функция? Она в цикле проверяет, равно ли имя класса предка имени заданного класса. Если да - значит, искомый класс потомок проверяемого. Если нет - проверяем уже родителя родителя. И так до тех пор, пока не дойдем до верхнего уровня.

Где применяется IsChildOf? Да в той же библиотеке, вот фрагмент кода:

procedure TEGPartsContainer.RedrawOwner;
var Image:TImage;
      LocationSize:integer;
      Axes:TEGAxesSystem;
begin
    if FOwner<>nil then
    begin
       if FOwner.IsChildOf('TEGLocationObject') then
       begin
          if FOwner.FOwner=nil then exit;
          if not(FOwner.FOwner.IsMap) then exit;
          Image:=TEGMap(FOwner.FOwner).Image;
          LocationSize:=TEGMap(FOwner.FOwner).LocationSize;
          Axes:=TEGMap(FOwner.FOwner).AxesSystem;
          TEGLocationObject(FOwner).DrawWithMap(Image,LocationSize,Axes);
       end;
   end;
end;

Объясняю какую роль здесь играет IsChildOf. Калсс TEGPartsContainer является контейнером составных частей объекта. Например, у объекта "стена" может быть составная часть"дверь" или "окно". У каждой составной части может быть владелец FOwner (у вышеназванной "двери" или "окна" это будет "стена"). В тот момент, когда нам надо перерисовать объект (например, дверь сменила состояние из "закрыта" в "открыта"), мы не знаем, можно ли владельца объекта перерисовать. Если да, то мы и перерисовываем его, с учетом новых состояний частей объекта. Если нет - ничего не делаем.

 


Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями  программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation".


Последнее обновление ( 05.06.2013 г. )