我一直在获得关于方法’创建’隐藏虚拟方法的Delphi编译器警告.

我已经回顾了几个Stack Overflow链接(见下文),我不明白这个警告背后的逻辑,以及为什么它被认为是错误的编码实践.我希望别人可以帮助我理解

我将包含一些示例代码:

type    
  TMachine = class(TPersistent)
  private
  public
    Horsepower : integer;
    procedure Assign(Source : TMachine);
  end;

...

procedure TMachine.Assign(Source : TMachine);
begin
  inherited Assign(Source);
  Self.Horsepower := Source.HorsePower;
end;

这会导致编译器警告.

[dcc32 Warning] Unit1.pas(21): W1010 Method 'Assign' hides virtual method of base type 'TPersistent'

我一直忽视这个警告,因为它对我没有任何意义.但这让我以另一种方式陷入困境(请参阅我在这里发表的另一篇文章:Why does Delphi call incorrect constructor during dynamic object creation?)所以我决定尝试更好地理解这一点.

我知道,如果我使用保留字重新引入,错误将消失,但我已经看到它反复发布这是一个坏主意.正如Warren P在这里写的那样(Delphi: Method ‘Create’ hides virtual method of base – but it’s right there),“恕我直言,如果你需要重新引入,你的代码闻起来很可怕”.

我想我明白“隐藏”是什么意思.正如David Heffernan在这里所说(What causes “W1010 Method ‘%s’ hides virtual method of base type ‘%s'” warning?):

What is meant by hiding is that from the derived class you no longer have access to the virtual method declared in the base class. You cannot refer to it since it has the same name as the method declared in the derived class. And that latter method is the one that is visible from the derived class.

但我有点困惑,因为似乎祖先方法并没有真正隐藏,因为派生类总是只能使用inherited关键字来调用基类中的方法.所以’隐藏’真的意味着’有些隐藏’吗?

我想我也明白使用保留字覆盖将阻止编译器警告,但过程签名必须相同(即没有新添加的参数).我不能在这里使用它.

我不明白的是为什么隐藏是值得警告的.在上面的代码示例中,我不希望TMachine.Assign()的用户以某种方式使用TPersistent.Assign().在我的扩展课程中,我扩展了需求,因此希望他们使用新的和改进的功能.因此,隐藏旧代码似乎正是我想要的.我对虚方法的理解是基于运行时对象的实际类型调用正确方法的方法.在这种情况下,我认为不应该有任何影响.

附加代码,将添加到上面的示例代码中

TAutomobile = class(TMachine)
  public
    NumOfDoors : integer;
    constructor Create(NumOfDoors,AHorsepower : integer);
  end;

...

constructor TAutomobile.Create(ANumOfDoors,AHorsepower : integer);
begin
  Inherited Create(AHorsepower);
  NumOfDoors := ANumOfDoors;
end;

这会添加新的编译器警告消息:[dcc32警告] Unit1.pas(27):W1010方法’Create’隐藏基类型’TMachine’的虚方法

我特别不理解使用带有附加参数的新构造函数时出现的问题.在这篇文章(SerialForms.pas(17): W1010 Method ‘Create’ hides virtual method of base type ‘TComponent’)中,智慧似乎应该引入具有不同名称的构造函数,例如CreateWithSize.这似乎允许用户选择他们想要使用的构造函数.

如果他们选择旧的构造函数,扩展类可能会缺少一些创建所需的信息.但是,如果相反,我“隐藏”了先前的构造函数,那就是编程错误. Marjan Venema写了关于重新引入同一链接:重新引入打破多态性.这意味着您不能再使用元类(TxxxClass = Tyyy类)来实例化您的TComponent后代,因为它的Create将不会被调用.我根本不明白这一点.

也许我需要更好地理解多态性.托尼·斯塔克在这个链接(What is polymorphism,what is it for,and how is it used?)中写道,多态性是:“面向对象编程的概念.不同对象以相同的方式以相同的方式响应的能力被称为多态.”那么我是否提出了一个不同的接口,即不再是相同的消息,从而打破了多态性?

我错过了什么?总之,在我的示例中,不是隐藏基本代码的好事吗?

解决方法

这里的危险是您可以在基类引用上调用Assign.因为您没有使用override,所以不会调用派生类方法.你已经破坏了多态性.

根据最少惊喜的原则,您应该在此处使用override,或者为您的派生类方法指定一个不同的名称.后一种选择很简单.前者看起来像这样:

type    
  TMachine = class(TPersistent)
  public
    Horsepower : integer;
    procedure Assign(Source : TPersistent); override;
  end;

...

procedure TMachine.Assign(Source : TPersistent);
begin
  if Source is TMachine then begin
    Horsepower := TMachine(Source).Horsepower;
  end else begin
    inherited Assign(Source);
  end;
end;

这允许您的班级与TPersistent的多态设计合作.不使用无法实现的覆盖.

您的下一个示例与虚拟构造函数类似.使构造函数成为虚拟的整个过程是,您可以在运行时直到创建实例而不知道它们的类型.规范示例是流式框架,即处理.dfm / .fmx文件并创建对象并设置其属性的框架.

流式传输框架依赖于TComponent的虚拟构造函数:

constructor Create(AOwner: TComponent); virtual;

如果希望组件使用流式框架,则必须覆盖此构造函数.如果你隐藏它,那么流式框架找不到你的构造函数.

考虑流式传输框架如何实例化组件.它不知道它需要使用的所有组件类.例如,它不能考虑第三方代码,即您编写的代码. Delphi RTL无法知道那里定义的类型.流式框架实例化这样的组件:

type
  TComponentClass = class of TComponent;

var
  ClassName: string;
  Classtype: TComponentClass;
  NewComponent: TComponent;

....
ClassName := ...; // read class name from .dfm/.fmx file
Classtype := GetClass(ClassName); // a reference to the class to be instantiated
NewComponent := Classtype.Create(...); // instantiate the component

Classtype变量包含一个元类.这允许我们表示直到运行时才知道的类型.我们需要调用Create以多态方式调度,以便执行组件构造函数中的代码.除非在声明构造函数时使用override,否则它不会.

实际上,所有这些归结为多态性.如果您对多态性的理解不如您所建议的那么坚定,那么您将很难理解这一点.我认为你的下一步行动是更好地掌握多态性.

oop – 隐藏基类的虚方法有什么问题?的更多相关文章

  1. Xcode C开发,需要澄清

    我非常喜欢Xcode提供对该语言可能的成员函数的深入了解的方式,并且更喜欢相对于文本伙伴使用它,如果不是因为我今天注意到的奇怪.当strings=“Teststring”时;唯一可用的substr签名如图所示但据我所知,签名应该是什么iseeonline确实s.substr(1,2);既被理解也适用于Xcode.当我尝试方法完成时为什么不显示?

  2. xamarin.ios – 没有找到ViewController ::.ctor(System.IntPtr)的构造函数

    我有一个问题,我的Monotouch应用程序有时在收到内存警告后才会崩溃.请参见下面的堆栈跟踪.堆栈跟踪是正确的,因为指定的类缺少构造函数获取IntPtr参数.但是这是有意的,因为我在应用程序中根本不使用InterfaceBuilder.那为什么会这样呢?

  3. ios – Swift – NSURL错误

    尝试使用下面的NSURL类时出错,下面的代码实际上是试图将我从Facebook拉入的图像存储到imageView中.错误如下:不知道为什么会这样,帮忙!解决方法你正在调用的NSURL构造函数有这个签名:?表示构造函数可能不返回值,因此它被视为可选.NSData构造函数也是如此:快速解决方法是:最好的解决方案是检查(解包)这些选项,即使您确定它们包含值!

  4. 如何在Xcode中追踪“libc abi.dylib:纯虚函数!”

    我有一个多线程OSX应用程序,它使用C,Objective-C和Swift的混合.当我的应用程序关闭时,我在Xcode调试器窗口中看到了这一点:我知道这个错误通常是由对C类构造函数或析构函数中的虚函数的调用引起的.有没有一种简单的方法可以找到它的位置?

  5. Swift实现对象归档

    Swift实现对象归档时有几个注意点要继承NSCoding,实现两个方法extension是一个分类,分类不允许有存储能力,所以协议方法不能写在分类中协议中的init(coderdecoder:NSCoder)函数会覆盖原始的构造函数,所以类中至少还要有另一个init方法如果不指定键名,会使用属性名称作为key,基本数据类型,需要指定key

  6. 【Swift初见】Swift构造过程

    构造过程是通过构造器来实现的,其实每个构造器就可以看作是一个函数,只是这个函数是为了执行初始化的。每个类都必须拥有一个指定构造器。

  7. Swift面向对象概念和基本特征

    面向对象是现代流行的程序设计方法,是主流的程序设计规范面向对象的基本特征包括:封装性,继承性和多态性:封装性:尽可能的隐藏对象的内部细节,对外形成一个边界,仅保留有限的对外接口使之与外部发生联系。

  8. swift的struct结构体类型介绍使用

  9. swift struct

    //:Playground-noun:aplacewherepeoplecanplayimportCocoavarstr="Hello,playground"structpoint{varx=0;vary=init(x:Int,y:Int){self.x=x;y=y;println("init");}funcgetCenter()->Int{return(x+y)/2;}mutatingfunca

  10. 《The Swift Programming Language》2.0版之自动引用计数

    Swift1.0文档翻译:TimothyYeSwift1.0文档校对:HawsteinSwift2.0文档校对及翻译润色:ChannePS:之前1.0版中文版看不懂地方在对比英文版后就懂了,还是之前翻译的不够准确啊。,而不是Person),它们的值会被自动初始化为nil,目前还不会引用到Person类的实例。由于Person类的新实例被赋值给了reference1变量,所以reference1到Person类的新实例之间建立了一个强引用。在你将john和number73赋值为nil后,强引用关系如下图:P

随机推荐

  1. delphi – 主窗口按进程名称处理

    DelphiXe,Win7x64如何从进程名称(exe文件的完整路径)获取主窗口句柄,或至少一个类或窗口名称(如果该进程只有一个窗口).例:解决方法我同意Petesh的说法,你需要枚举顶级窗口并检查创建它的进程的模块文件名.为了帮助您开始枚举顶级窗口,这是一个delphi实现.首先,当你回调给你时,你需要一些与EnumWindows方法通信的方式.为此声明一条记录,该记录将保存您要查找的模块的文件

  2. 如何在Delphi中纯粹通过RTTI信息(即不使用任何实际对象实例)获取TObjectList的子项类型?

    我正在使用RTTI实现用于流式传输任意Delphi对象的通用代码,并且为了使其工作(更具体地说,为了使加载部分工作),我需要以某种方式获得TObjectList的子项类型<T>不使用任何实际对象实例的字段.要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用–我宁愿只能访问相关类的纯RTTI数据.我希望能

  3. inno-setup – Inno Setup – 安装程序背景图片

    图像作为安装程序背景如何用inno5.5.9做到这一点?

  4. inno-setup – Inno Setup – 如何添加多个arc文件进行解压缩?

    使用InnoSetup解压缩弧文件.我希望有可能解压缩多个arc文件以从组件选择中安装文件(例如).但仍然显示所有提取的整体进度条.这可能吗?的回答的修改预备是相同的,参考其他答案.在ExtractArc中,为要提取的每个存档调用AddArchive.

  5. delphi – 如何在DataSet的帮助下在TAdvStringGrid中显示数据库中的BLOB图像

    解决方法CreateBlobStream正在创建一个TStream对象,而不是TMemoryStream.由于您不想将JPG写入数据库,因此应使用bmRead而不是bmReadWrite.我不习惯sqlite,但你必须确保使用合适的二进制日期类型.为了确保存储的图像真的是JPG,您应该编写JPG以进行测试,例如:

  6. inno-setup – 在Inno Setup的Code部分下载程序后运行程序

    如何运行我通过Internet下载的应用程序,在代码部分中使用,并等待该应用程序完成运行.我有,使用InnoTools下载程序,下载这两个文件,我想,在第二个完成下载后运行该下载,或jdk-8u111-windows-x64.exe,然后继续安装.解决方法使用其他下载插件,而不是ITD(请参阅下面的原因).例如,InnoDownloadPlugin.当您包含idp.iss时,它定义了一个全局IDP

  7. progress-bar – Inno Setup Run部分的简单进度页面

    我的安装程序非常简单,它基本上是:>欢迎页面>进展页面>最终页面欢迎页面和最终页面是标准页面.在Progress页面,我正在静默安装一堆其他程序.实际的脚本是在[Run]部分中安装每个程序.问题是酒吧达到100%然后停留在那里.我只能更改消息文本.我想要实现的是使用Pascal脚本显示进度,例如:这样我就可以显示更准确的进度条.这就是我所拥有的:问题是,当我构建安装程序时,它不显示欢迎页面.我做错了什么?

  8. delphi – 如何使“显示/隐藏桌面图标”设置生效?

    下面的代码调用SHGetSetSettings函数来隐藏桌面图标但它只是从视图菜单中取消选中“显示桌面图标”.我打电话给SHChangeNotify;更新桌面,但这不起作用?解决方法isa,要刷新桌面,您可以将F5键发送到progman窗口隐藏桌面图标的另一种方法是再次显示

  9. inno-setup – Inno Setup – 避免显示子安装程序的文件名

    我试图使用InnoSetup–Howtohidecertainfilenameswhileinstalling?(FilenameLabel)的想法Theonlysuresolutionistoavoidinstallingthefiles,youdonotwanttoshow,usingthe[Files]section.Installthemusingacodeinstead.UsetheEx

  10. inno-setup – Inno Setup磁力链接下载实施

    我目前正在使用InnoDownloadPlugin为我的安装程序下载文件,这个问题最大的问题是faila正确下载文件.因为连接不良等诸多原因.我想添加一种替代方法来下载文件,因此用户可以选择是否需要常规方式或torrent方式.我知道我可以使用aria2c.exe应用程序(https://aria2.github.io/),有人可以帮我实现它的inno设置代码吗?我需要的是使用torrent(ar

返回
顶部