为什么在重载基类中引入的抽象方法时编译器会发出警告?

4

我有这样的代码:

TBaseClass = class(TObject)
protected
  procedure aMethod(const s:string);virtual;abstract;
end;

TDerivedClass = class(TBaseClass)
protected
   procedure aMethod(const s:string);overload;override;
   procedure aMethod(const s:string;const x:integer);overload;
end;

编译器生成警告:

[DCC 警告].... W1010 方法“aMethod”隐藏了基类型“TBaseClass”的虚方法

点击警告会跳转到“aMethod(const s:string;const x:integer);”,因为它没有标记override指令。然而,这个方法不能被标记为override:在基类中不存在该签名的方法,将override指令添加到该方法会导致编译器错误:

[DCC 错误].... E2037 “aMethod”的声明与前一个声明不同。

由于TBaseClass中不存在该签名的方法,所以这是显而易见的。

只有在基类中存在'aMethod(const s:string)',并且该方法被标记为'override' - 因此基类中没有被隐藏的内容。

为什么这不是一个错误的警告?(这不是我遇到的第一个,甚至不是最后一个...)

对其他问题的参考是不正确的,我的意见如下。 我有解决方案- 我简单地使用重构,重命名了有问题的方法。 但我不是在寻找解决方案,这很琐碎。我想要的是对这个警告的解释。 这个设计有什么问题吗?(也许同时使用overload和override不是好的设计 - 我可以同意这一点,但这不是编译器警告的真正原因。)


@J...:这不是重复的问题。他接受了一个解决方案。我有一个解决方案——但那不是我要找的。我要找的是一个解释。 - Vector
1
简短回答:基类方法签名不包括“overload”,因此任何重载它的派生类都会引入对该方法签名的更改,并且“隐藏”了祖先的定义。在TBaseClass中将aMethod标记为overload;virtual;abstract;,编译器警告就会消失(即使TBaseClass没有定义任何重载变体)。 - J...
@J...:哦,你说得对。我刚试了一下,没想到它会编译通过,因为基类中没有重载的方法。但它确实编译通过了,事实上,“overload”似乎总是能编译通过,无论在哪里使用它。(我很少使用它——不喜欢它——而是更喜欢重新命名方法)我仍然认为警告是不正确的,因为由于它被重载,没有任何东西被隐藏——原始调用仍然可以从派生类中调用——但显然名称的重复使用足以生成警告——这才是问题的真正答案。将您的评论发布为答案并获得积分。 - Vector
Remy已经发布了基本相同的答案,我不想重复;) - J...
@J...:我看到了Remy的回答,但他好像遇到了其他问题 - 他说它只在XE3上运行。此外,他已经有足够的积分了。 :-) - Vector
显示剩余3条评论
1个回答

4
最近我在Indy中遇到了同样的问题。其基类TIdStack有抽象的GetSocketOption()SetSocketOption()方法,TIdStackBDSBase则会覆盖并重载这些方法以供其派生类(TIdStackWindows等)重写。我遇到了与此完全相同的编译错误。
例如:
type
  TIdStack = class(TObject)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle;
      ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption;
      out AOptVal: Integer); virtual; abstract;
    ...
  end;

.

type
  TIdStackBSDBase = class(TIdStack)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
      AOptName: TIdSocketOption; out AOptVal: Integer); overload; override;
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
      AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); overload; virtual; abstract;
    ...
  end;

procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle;
  ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer);
var
  LBuf, LLen: Integer;
begin
  LLen := SizeOf(LBuf);
  GetSocketOption(ASocket, ALevel, AOptName, LBuf, LLen);
  AOptVal := LBuf;
end;

.

type
  TIdStackWindows = class(TIdStackBSDBase)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
      AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
    ...
  end;

procedure TIdStackWindows.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
begin
  ...
end;

无论是否将 TIdStack.GetSocketOption() 声明为 overload,XE2 都会报告以下错误:

[DCC Error] IdStackWindows.pas(296): E2137 Method 'GetSocketOption' not found in base class

原来在某些情况下(比如Indy的情况),编译器要求基类方法声明为overload,即使在基类本身中没有对应的重载方法,才能让派生类覆盖和重载它。
然而,当我这么做时,在XE2及更早版本中无法正常工作,会出现“隐藏虚方法”警告和其他错误。在XE3中似乎已经修复了这个问题。所以,我最终在Indy中所做的是:
1. 将基类TIdStack方法声明为overload; virtual; abstract; 2. 在TIdStackBDSBase中,将被覆盖的方法声明为overload; override;,然后: a. 在XE2及更早版本中,将重载方法声明为reintroduce; overload;,并为后代声明独立的非重载virtual; abstract;方法供其override。 b. 在XE3及更高版本中,将重载方法声明为overload; virtual; abstract;,并让后代正常override它们。
换句话说,以下代码在XE3中有效,但在XE2中无效:
type
  TIdStack = class(TObject)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; virtual; abstract;
    ...
  end;

.

type
  TIdStackBSDBase = class(TIdStack)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; override;
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); overload; virtual; abstract;
    ...
  end;

  procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer);
  var
    LBuf, LLen: Integer;
  begin
    LLen := SizeOf(LBuf);
    GetSocketOption(ASocket, ALevel, AOptName, LBuf, LLen);
    AOptVal := LBuf;
  end;

.

type
  TIdStackWindows = class(TIdStackBSDBase)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
    ...
  end;

  procedure TIdStackWindows.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
  begin
    ...
  end;

以下代码在XE2中可以工作:
```html

以下代码在XE2中可以工作:

```
type
  TIdStack = class(TObject)
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; virtual; abstract;
    ...
  end;

.

type
  TIdStackBSDBase = class(TIdStack)
    ...
    procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); virtual; abstract;
    ...
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; override;
    procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); reintroduce; overload;
    ...
  end;

procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer);
var
  LBuf, LLen: Integer;
begin
  LLen := SizeOf(LBuf);
  WSGetSocketOption(ASocket, ALevel, AOptName, LBuf, LLen);
  AOptVal := LBuf;
end;

procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
begin
  WSGetSocketOption(ASocket, ALevel, AOptName, AOptVal, AOptLen);
end;

.

type
  TIdStackWindows = class(TIdStackBSDBase)
    ...
    procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
    ...
  end;

  procedure TIdStackWindows.WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
  begin
    ...
  end;

通过基类 overload; virtual; abstract; 和派生类 overload; override(用于声明的基类方法)和 overload;(用于其余方法),我在XE2或D2010上不会收到“隐藏虚拟”编译器警告... - J...
同样的情况也发生在这里 - 它向基本声明添加了重载,XE-1中一切都很好。 - Vector
@RemyLebeau,我仍然没有问题,使用您发布的模式:基本方法重载/虚拟/抽象,派生类重载/覆盖(基本方法)- 重载/虚拟/抽象(扩展参数),第二派生类覆盖虚拟扩展参数方法...在D2010或XE2中没有警告。 - J...
我正在使用XE2升级4版,你用的是什么?我曾经也遇到过Indy用户抱怨同样的问题,所以这不仅仅是我的问题。这就是为什么我必须按照我现在的方式重新编写Indy的代码,而且现在已经可以工作了。 - Remy Lebeau
@RemyLebeau 我相信你,但同时无论问题是什么,它似乎不是由这个模式引起的。 Indy 必须有其他问题。 也许我会在某个时候看一下。 - J...
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接