class – 接口方法总是虚拟的吗?

编译以下代码时出现错误:

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

[dcc64 Error] OtlParallel.pas(846): E2170 Cannot override a non-virtual method

如果我将祖先方法设为虚拟,那么错误就会消失.

但是祖先方法声明在:

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

TOmniParallelSimpleLoop中的基本方法从非虚拟到虚拟的重新声明是否会更改基类型,或者是已经虚拟的方法(由于它是接口方法的实现)?

换句话说:当接口方法从非虚拟变为虚拟时,编译器会输出不同的代码吗?

基本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.
最佳答案

Are interface methods always virtual?

接口的方法既不是虚拟的也不是非虚拟的.该概念不适用于接口方法.

另一方面,类的方法可以是虚拟的或非虚拟的.区别决定了方法调用的绑定方式.虚拟方法在运行时绑定,考虑到对象的运行时类型.并且使用对象引用的编译时类型在编译时绑定非虚方法.

编译器错误只是告诉您覆盖仅对虚拟方法有意义.您的代码正在尝试对非虚拟方法使用覆盖.考虑一下这个程序,它根本不包含任何接口:

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.

此程序无法编译与您的程序完全相同的错误.该错误与接口无关.在T1中引入DoSomething时将其声明为虚拟将解决该错误.

Will the redeclaration of the base method in TOmniParallelSimpleLoop from non-virtual to virtual change the base type?

是的,它会的.它将该方法从非虚拟更改为虚拟.这意味着如上所述,方法调度以不同方式执行.这意味着类型的VMT会更改以适应新的虚拟方法.

Or was the method already virtual to begin with (due to it being an implementation of an interface method)?

使用方法实现接口的一部分这一事实并不会改变编译器处理它的方式.无论是否实现接口方法,都以相同的方式实现非虚方法.同样适用于虚拟方法.为实现接口而生成的VMT是一个独特的问题.

详细说明,每种方法都有一个地址.在调用非虚方法时,编译器确切地知道要调用哪个方法.因此它可以发出代码直接调用该已知方法.对于虚方法,编译器不知道将调用哪个方法.这由运行时类型决定.因此编译器发出代码来读取对象VMT中的已知条目,然后调用该方法.

现在,我相信您也知道,接口也是使用VMT实现的.但这并不意味着实现方法会自动升级为虚拟.界面只是一个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调用方法的事实并不意味着必须以这种方式调用它.

转载注明原文:class – 接口方法总是虚拟的吗? - 代码日志