class - 接口(interface)方法总是虚拟的吗?

标签 class delphi interface virtual

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

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 中的基方法从非虚拟重新声明为虚拟是否会更改基本类型,或者该方法一开始就已经是虚拟的(因为它是接口(interface)方法的实现) )?

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

重现错误的基本 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?

接口(interface)的方法既不是虚拟的也不是非虚拟的。这个概念不适用于接口(interface)的方法。

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

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

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.

该程序无法编译,并出现与您的程序完全相同的错误。该错误与接口(interface)无关。在 T1 中引入 DoSomething 时将其声明为虚拟将解决该错误。

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

是的,会的。它将该方法从非虚拟更改为虚拟。这意味着方法分派(dispatch)的执行方式不同,如上所述。这意味着该类型的 VMT 会发生变化以适应新的虚拟方法。

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

方法用于实现接口(interface)的一部分这一事实不会改变编译器处理它的方式。非虚方法无论是否实现接口(interface)方法,其实现方式都是相同的。对于虚拟方法也是如此。为实现该接口(interface)而生成的 VMT 是一个明显的问题。

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

现在,我相信您知道,接口(interface)也是使用 VMT 实现的。但这并不意味着实现方法会自动升级为虚拟的。接口(interface)就是一个 VMT。当被视为类的方法时,接口(interface) 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 - 接口(interface)方法总是虚拟的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36137977/

相关文章:

c - Delphi记录: can we do aggregate assignment?

delphi - Indy 10 FTP 空列表

delphi - Formatfloat 是如何工作的

c# - 返回接口(interface)但具体可能具有不在接口(interface)上的属性,我可以通过强制转换获得它们吗?

Python,当类型(a)仍然是A时如何处理A(a)

python - 如何从 main 引用嵌套在类中的定义变量

c++ - 将结构转换为类?

javascript - 在实例对象内的函数中获取类(this)的实例 - typescript /Angular

java - 如何提供跨平台、异步的服务接口(interface)

java - 泛型 : Inheriting from an abstract class that implements an interface