【问题标题】:Why does the compiler warn when overloading an abstract method introduced in the base class?为什么编译器在重载基类中引入的抽象方法时会发出警告?
【发布时间】:2013-05-02 16:42:56
【问题描述】:

我有这样的代码:

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);'因为它没有用覆盖指令标记。但是,该方法 不能 被标记为覆盖:基类中不存在具有该签名的方法,并且将覆盖指令添加到该方法会导致编译器错误:

[DCC 错误].... E2037 'aMethod' 的声明与之前的声明不同。

这很明显,因为 TBaseClass 中不存在具有该签名的方法。

基类中仅存在“aMethod(const s:string)”,并且该方法被标记为“覆盖” - 因此根本没有隐藏基类中的任何内容。

为什么这不是一个错误的警告?(也不是我遇到的第一个警告......)

IMO,对另一个问题的引用不正确。 我有一个解决方案 - 我只是使用了重构,并重命名了有问题的方法。 但我不是在寻找一个微不足道的解决方案。我正在寻找对这个警告的解释。这个设计有问题吗? (也许同时使用重载和覆盖并不是好的设计——我同意这一点,但这并不是编译器警告的真正含义。)

【问题讨论】:

  • @J... :这不是骗子。他接受了一个解决方案。我有一个解决方案——这不是我想要的。我正在寻找解释。
  • 在 cmets 中跟进 - 我链接到最佳解决方案,可能是问题“为什么?”的最佳答案在这里,但是(也在标记的欺骗中链接)stackoverflow.com/a/58167/327083
  • 简而言之,基类方法签名不包括overload,因此任何重载它的派生类都会对方法签名进行更改并“隐藏”祖先的定义。在TBaseClass 中将aMethod 标记为overload;virtual;abstract;,编译器警告就会消失(即使TBaseClass 没有定义任何重载变体)。
  • @J... :嗯 - 你是对的。我只是尝试过 - 认为它不会编译,因为基类中没有重载方法。但它确实做到了——事实上,“重载”似乎总是可以编译,无论你在哪里使用它。 (我很少使用它 - 不喜欢它 - 而是重新命名该方法)我仍然认为警告是不正确的,因为它已重载,没有任何内容被隐藏 - 原始调用仍然可以从派生类调用 - 但显然重用名称就足以产生警告——这是问题的真正答案。发表您的评论作为答案并获得积分。

标签: delphi inheritance delphi-xe compiler-warnings


【解决方案1】:

我最近在 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 中修复。所以我最终不得不在印地做的是:

  1. 将基本的TIdStack 方法声明为overload; virtual; abstract;

  2. TIdStackBDSBase中,将覆盖的方法声明为overload; override;,然后:

    一个。在 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 中有效:

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...
  • 这里也一样 - 它为基本声明添加了重载,并且在 XE-1 中一切正常。
  • 我已经用导致 XE2 出错但在 XE3 中工作的确切代码更新了我的答案,以及我必须在 XE2 中使用什么代码才能使其工作。
  • @RemyLebeau 我对您发布的模式仍然没有问题:基方法重载/虚拟/抽象,派生类重载/覆盖(基础方法)-重载/虚拟/抽象(扩展参数),虚拟扩展参数方法的第二个派生类覆盖...在 D2010 或 XE2 中没有警告
  • 我用的是XE2 Update 4,你用的是什么?而且我有 Indy 用户抱怨过同样的问题,所以不仅仅是我。这就是为什么我必须按照我的方式重写 Indy 的代码,这很有效。
猜你喜欢
  • 2011-10-31
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-17
  • 2017-09-29
  • 1970-01-01
  • 2011-08-20
相关资源
最近更新 更多