Методы интерфейса всегда виртуальные?

при компиляции следующего кода я получаю сообщение об ошибке:

TOmniParallelSimplePooledLoop = class(TOmniParallelSimpleLoop)
  procedure Execute(loopBody: TOmniIteratorSimpleSimpleDelegate); overload; override;

[ошибка dcc64] OtlParallel.pas (846): E2170 не может переопределить не виртуальный метод

если я сделаю метод предка виртуальным, то ошибка исчезнет.

однако метод предка объявлен в:

IOmniParallelSimpleLoop
  ...
  procedure Execute(loopBody: TOmniIteratorSimpleSimpleDelegate); overload;

будет ли повторное объявление базового метода в TOmniParallelSimpleLoop от не-виртуального к виртуальному измените базовый тип, или метод уже был виртуальным для начала (из-за того, что это реализация метода интерфейса)?

другими словами: Будет ли компилятор выводить другой код, когда сопряженный метод изменяется с не виртуального на виртуальный?

Basic MSVC для воссоздания ошибки

program Project70;
{$APPTYPE CONSOLE}
uses
  System.SysUtils;

type
  I1 = interface
    procedure DoSomething;
  end;

  T1 = class(TInterfacedObject, I1)
    procedure DoSomething;
  end;

  T2 = class(T1)
    procedure DoSomething; override;
  end;

procedure T1.DoSomething;
begin
  WriteLn('parent');
end;

procedure T2.DoSomething;
begin
  Writeln('Child');
end;

begin
end.

2 ответов


являются ли методы интерфейса всегда виртуальными?

методы интерфейсов не являются ни виртуальными, ни не виртуальными. Эта концепция не применяется к методам интерфейсов.

С другой стороны, методы классов могут быть виртуальные или не виртуальные. Различие определяет, как связаны вызовы метода. Виртуальные методы привязываются во время выполнения с учетом типа выполнения объекта. И не-виртуальные методы связаны во время компиляции, используя тип времени компиляции ссылки на объект.

ошибка компилятора просто говорит вам, что override имеет смысл только для виртуальных методов. Ваш код пытается использовать override для метода, который не является виртуальным. Рассмотрим эту программу, которая вообще не содержит никаких интерфейсов:

type
  T1 = class
    procedure DoSomething;
  end;

  T2 = class(T1)
    procedure DoSomething; override;
  end;

procedure T1.DoSomething;
begin
end;

procedure T2.DoSomething;
begin
end;

begin
end.

эта программа не компилируется с той же самой ошибкой, что и ваша программа. Ошибка не связана с интерфейсами. Объявление DoSomething быть виртуальным, когда он вводится в T1 устранить ошибку.

будет ли повторное объявление базового метода в TOmniParallelSimpleLoop от не-виртуального к виртуальному измените базовый тип?

Да, это будет. Он изменяет этот метод с non-virtual на virtual. Это означает, что метод dispatch выполняется по-разному, как описано выше. Это означает, что VMT типа изменяется для размещения нового виртуального метода.

или метод уже был виртуальным для начала (из-за него будучи реализацией метода интерфейса)?

тот факт, что метод используется для реализации части интерфейса не меняется, как компилятор обрабатывает его. Не виртуальный метод реализуется таким же образом, независимо от того, реализует ли он метод интерфейса или нет. Аналогично для виртуального метода. VMT, созданный для реализации интерфейса, является отдельной проблемой.

уточняя, что каждый метод имеет адрес. При вызове не виртуального метода компилятор точно знает, какой метод вызывать. Таким образом, он может излучать код для вызова этого известного метода напрямую. Для виртуального метода компилятор не знает, какой метод будет вызван. Это определяется типом среды выполнения. Таким образом, компилятор выдает код для чтения известной записи в VMT объекта, а затем вызывает этот метод.

теперь, как я уверен, вы знаете, интерфейсы также реализованы с помощью VMTs. Но это не означает, что методы реализации автоматически повышаются до уровня виртуальный. Интерфейс-это просто VMT. Методы, на которые ссылается интерфейс VMT, могут быть виртуальными или не виртуальными, если рассматривать их как методы класса.

type
  ISomeInterface = interface
    procedure Foo;
  end;

  TSomeObject = class(TInterfacedObject, ISomeInterface)
    procedure Foo;
  end;

....

var
  Intf: ISomeInterface;
  Obj: TSomeObject;
....
Intf := TSomeObject.Create;
Obj := Intf as TSomeObject;

// non-virtual method, direct dispatch at compile time
Obj.SomeMethod; 

// interface method, dispatched via interface VMT
Intf.SomeMethod;

таким образом, тот факт, что метод может быть вызван через VMT, не означает, что он должен быть вызван таким образом.


Я думаю, что ответ на это содержится в одном предложении "Delphi Component Design" Дэнни Торпа, ISBN 0-201-46136-6, я упомянул в комментарии к ответу @DavidH:

"VMT-это именно массив указателей функций", в разделе" импорт объектов из DLL - трудный путь", стр. 89.

в следующем разделе "импорт объектов из DLL-умный способ" объясняется, как массив указателей функций для членов абстрактного интерфейса может использоваться для вызовите DLL, которая реализует интерфейс и как это (гениальный ход, а не только imo) обеспечило основу поддержки com Delphi (как только она прошла поддержку через варианты D2).