Delphi:理解构造函数 [英] Delphi: Understanding constructors

查看:32
本文介绍了Delphi:理解构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望理解

  • 虚拟
  • 覆盖
  • 过载
  • 重新介绍

应用于对象构造函数时.每次我随机添加关键字直到编译器关闭 - 而且(在使用 Delphi 开发 12 年后)我宁愿知道我在做什么,而不是随机尝试.

when applied to object constructors. Every time i randomly add keywords until the compiler shuts up - and (after 12 years of developing with Delphi) i'd rather know what i'm doing, rather than trying things randomly.

给定一组假设的对象:

TComputer = class(TObject)
public
    constructor Create(Cup: Integer); virtual;
end;

TCellPhone = class(TComputer)
public
    constructor Create(Cup: Integer; Teapot: string); virtual;
end;

TiPhone = class(TCellPhone)
public
    constructor Create(Cup: Integer); override;
    constructor Create(Cup: Integer; Teapot: string); override;
end;

我希望他们的行为方式从声明中可能很明显,但是:

The way i want them to behave is probably obvious from the declarations, but:

  • TComputer 有简单的构造函数,后代可以覆盖它
  • TCellPhone 有一个替代构造函数,后代可以覆盖它
  • TiPhone 覆盖两个构造函数,调用每个构造函数的继承版本
  • TComputer has the simple constructor, and descendants can override it
  • TCellPhone has an alternate constructor, and descendants can override it
  • TiPhone overrides both constructors, calling the inherited version of each

现在该代码无法编译.我想了解为什么它不起作用.我也想了解覆盖构造函数的正确方法.或者也许你永远无法覆盖构造函数?或者也许重写构造函数是完全可以接受的?或许你永远不应该拥有多个构造函数,或许拥有多个构造函数是完全可以接受的.

Now that code doesn't compile. i want to understand why it doesn't work. i also want to understand the proper way to override constructors. Or perhaps you could never override constructors? Or perhaps it is perfectly acceptable to override constructors? Perhaps you should never have multiple constructors, perhaps it is perfectly acceptable to have multiple constructors.

我想了解为什么.修复它就很明显了.

i want to understand the why. Fixing it would then be obvious.

我还想对 virtualoverrideoverload 的顺序进行一些推理>, 重新引入.因为在尝试所有关键字组合时,组合数量爆炸式增长:

i'm also looking to get some reasoning on the order of virtual, override, overload, reintroduce. Because when trying all combinations of keywords, the number of combinations explodes:

  • 虚拟的;超载;
  • 虚拟的;覆盖;
  • 覆盖;超载;
  • 覆盖;虚拟;
  • 虚拟的;覆盖;超载;
  • 虚拟的;超载;覆盖;
  • 过载;虚拟的;覆盖;
  • 覆盖;虚拟的;超载;
  • 覆盖;超载;虚拟;
  • 过载;覆盖;虚拟;

编辑 2:我想我们应该从给出的对象层次结构是否可能?"如果不是,为什么不呢?例如,从祖先那里获得构造函数从根本上是错误的吗?

Edit 2: i guess we should begin with "is the object hierarchy given even possible?" If not, why not? For example, is it fundamentally incorrect to have a constructor from an ancestor?

TComputer = class(TObject)
public
    constructor Create(Cup: Integer); virtual;
end;

TCellPhone = class(TComputer)
public
    constructor Create(Cup: Integer; Teapot: string); virtual;
end;

我希望 TCellPhone 现在有两个构造函数.但是我在 Delphi 中找不到关键字的组合,使它认为这是一件有效的事情.我认为我可以在 TCellPhone 中有两个构造函数从根本上是错误的吗?

i would expect that TCellPhone now has two constructors. But i can't find the combination of keywords in Delphi to make it think that's a valid thing to do. Am i fundamentally wrong in thinking i can have two constructors here in TCellPhone?

注意:此行以下的所有内容都不是回答问题所必需的问题 - 但它确实有助于解释我的想法.或许你能看到,根据我的思维过程,什么我想念的基本部分让一切都清楚.

Note: Everything below this line is not strictly needed to answer the question - but it does help to explain my thinking. Perhaps you can see, based on my thought processes, what fundamental piece i'm missing that makes everything clear.

现在这些声明无法编译:

Now these declarations don't compile:

//Method Create hides virtual method of base type TComputer:
TCellPhone = class(TComputer)
   constructor Create(Cup: Integer; Teapot: string);  virtual;

//Method Create hides virtual method of base type TCellPhone:
TiPhone = class(TCellPhone)
public
   constructor Create(Cup: Integer); override;
   constructor Create(Cup: Integer; Teapot: string); overload;  <--------
end;

<小时>

所以首先我会尝试修复TCellPhone.我将首先随机添加 overload 关键字(我知道我不想要 reintroduce 因为这会隐藏我不想要的另一个构造函数):


So first i'll trying fixing TCellPhone. i'll start by randomly adding the overload keyword (i know i don't want reintroduce because that would hide the other constructor, which i don't want):

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer; Teapot: string); virtual; overload;
end;

但是失败了:在方法或属性之后不允许定义字段.

我从经验中知道,即使我在方法或属性之后没有字段,如果我颠倒 virtualoverload 关键字的顺序:德尔福会闭嘴:

i know from experience that, even though i don't have a field after a method or property, if i reverse the order of the virtual and overload keywords: Delphi will shut up:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
end;

但我仍然收到错误:

方法Create"隐藏基类型TComputer"的虚方法

Method 'Create' hides virtual method of base type 'TComputer'

所以我尝试删除两个关键字:

So i try removing both keywords:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer; Teapot: string);
end;

但我仍然收到错误:

方法Create"隐藏基类型TComputer"的虚方法

Method 'Create' hides virtual method of base type 'TComputer'

所以我辞职现在尝试重新引入:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer; Teapot: string); reintroduce;
end;

现在 TCellPhone 可以编译,但它使 TiPhone 的情况变得更糟:

And now TCellPhone compiles, but it has made things much worse for TiPhone:

TiPhone = class(TCellPhone)
public
   constructor Create(Cup: Integer); override; <-----cannot override a static method
   constructor Create(Cup: Integer; Teapot: string); override; <-----cannot override a static method
end;

两者都抱怨我无法覆盖它们,所以我删除了 override 关键字:

Both are complaining that i cannot override them, so i remove the override keyword:

TiPhone = class(TCellPhone)
public
   constructor Create(Cup: Integer);
   constructor Create(Cup: Integer; Teapot: string);
end;

但是现在第二个创建说它必须标记为超载,我这样做(实际上我将两者都标记为超载,因为我知道如果我不这样做会发生什么):

But now the 2nd create says it must be marked with overload, which i do (in fact i'll mark both as overload, since i know what will happen if i don't):

TiPhone = class(TCellPhone)
public
   constructor Create(Cup: Integer); overload;
   constructor Create(Cup: Integer; Teapot: string); overload;
end;

interface 部分的一切都很好.不幸的是,我的实现不起作用.我的TiPhone单参数构造函数不能调用继承的构造函数:

All all is good in the interface section. Unfortunately my implementations won't work. My single parameter constructor of TiPhone cannot call the inherited constructor:

constructor TiPhone.Create(Cup: Integer);
begin
    inherited Create(Cup); <---- Not enough actual parameters
end;

推荐答案

我认为您的原始声明集不能干净地编译有两个原因:

I see two reasons your original set of declarations shouldn't compile cleanly:

  1. TCellPhone 中应该有一个警告,它的构造函数隐藏基类的方法.这是因为基类方法是虚拟,编译器担心您会在不覆盖基类方法的情况下引入具有相同名称的方法.签名不同并不重要.如果您确实打算隐藏基类的方法,那么您需要在后代声明上使用 reintroduce ,正如您的盲目猜测之一所示.该指令的唯一目的是平息警告;它对运行时行为没有影响.

  1. There should be a warning in TCellPhone that its constructor hides the method of the base class. This is because the base-class method is virtual, and the compiler worries that you're introducing a new method with the same name without overriding the base-class method. It doesn't matter that the signatures differ. If your intention is indeed to hide the method of the base class, then you need to use reintroduce on the descendant declaration, as one of your blind guesses showed. The sole purpose of that directive is to quell the warning; it has no effect on run-time behavior.

忽略TIPhone 稍后会发生什么,下面的TCellPhone 声明就是您想要的.它隐藏了祖先方法,但您也希望它是虚拟的.它不会继承祖先方法的虚拟性,因为它们是两个完全独立的方法,只是碰巧具有相同的名称.因此,您还需要在新声明中使用 virtual.

Ignoring what's going to happen with TIPhone later on, the following TCellPhone declaration is what you'd want. It hides the ancestor method, but you want it to be virtual as well. It won't inherit the virtualness of the ancestor method because they're two completely separate methods that just happen to have the same name. Therefore, you need to use virtual on the new declaration as well.

TCellPhone = class(TComputer)
public
  constructor Create(Cup: Integer; Teapot: string); reintroduce; virtual;
end;

基类构造函数TComputer.Create,也隐藏了祖先的一个方法,TObject.Create,但由于TObject 中的方法不是虚拟的,编译器不会对此发出警告.隐藏非虚拟方法一直在发生,通常没什么特别的.

The base-class constructor, TComputer.Create, is also hiding a method of its ancestor, TObject.Create, but since the method in TObject is not virtual, the compiler doesn't warn about it. Hiding non-virtual methods happens all the time and is generally unremarkable.

你应该在 TIPhone 中得到一个 error 因为不再有任何单参数构造函数来覆盖.您将其隐藏在 TCellPhone 中.由于您想要有两个构造函数,重新引入 显然不是早先使用的正确选择.您不想隐藏基类构造函数;你想用另一个构造函数来扩充它.

You should get an error in TIPhone because there is no longer any one-argument constructor to override. You hid it in TCellPhone. Since you want to have two constructors, reintroduce clearly wasn't the right choice to use earlier. You don't want to hide the base-class constructor; you want to augment it with another constructor.

由于您希望两个构造函数具有相同的名称,因此您需要使用 overload 指令.该指令需要用于所有原始声明 - 第一次引入每个不同的签名 后代中的后续声明.我认为 all 声明(甚至是基类)都需要它,这样做并没有什么坏处,但我想这不是必需的.因此,您的声明应如下所示:

Since you want both constructors to have the same name, you need to use the overload directive. That directive needs to be used on all the original declarations — the first time each distinct signature is introduced subsequent declarations in descendants. I thought it was required on all declarations (even the base class), and it doesn't hurt to do that, but I guess it's not required. So, your declarations should look like this:

TComputer = class(TObject)
public
  constructor Create(Cup: Integer);
    overload; // Allow descendants to add more constructors named Create.
    virtual;  // Allow descendants to re-implement this constructor.
end;

TCellPhone = class(TComputer)
public
  constructor Create(Cup: Integer; Teapot: string);
    overload; // Add another method named Create.
    virtual;  // Allow descendants to re-implement this constructor.
end;

TiPhone = class(TCellPhone)
public
  constructor Create(Cup: Integer);
    override; // Re-implement the ancestor's Create(Integer).
  constructor Create(Cup: Integer; Teapot: string);
    override; // Re-implement the ancestor's Create(Integer, string).
end;

现代文档说明一切应该按照什么顺序进行:

Modern documentation tells what order everything should go in:

重新引入过载绑定;调用约定抽象警告

其中binding虚拟动态覆盖调用约定registerpascalcdeclstdcall安全呼叫;并且警告平台弃用.

where binding is virtual, dynamic, or override; calling convention is register, pascal, cdecl, stdcall, or safecall; and warning is platform, deprecated, or library.

这是六个不同的类别,但根据我的经验,任何声明中很少有超过三个类别.(例如,需要指定调用约定的函数可能不是方法,因此它们不能是虚拟的.)我从来不记得顺序;直到今天,我才看到它被记录在案.相反,我认为记住每个指令的目的更有帮助.当您记住不同任务需要哪些指令时,您最终只会得到两三个指令,然后通过试验获得有效订单非常简单.编译器可能会接受多个顺序,但不要担心——顺序在确定含义方面并不重要.编译器接受的任何顺序都与其他任何顺序具有相同的含义(调用约定除外;如果您提到的不止一个,则只计算最后一个,所以不要这样做).

Those are six different categories, but in my experience, it's rare to have more than three on any declaration. (For example, functions that need calling conventions specified probably aren't methods, so they can't be virtual.) I never remember the order; I've never seen it documented till today. Instead, I think it's more helpful to remember each directive's purpose. When you remember which directives you need for different tasks, you'll end up with just two or three, and then it's pretty simple to experiment to get a valid order. The compiler might accept multiple orders, but don't worry — order isn't important in determining meaning. Any ordering the compiler accepts will have the same meaning as any other (except for calling conventions; if you mention more than one of those, only the last one counts, so don't do that).

所以,那么您只需要记住每个指令的目的,并考虑哪些指令放在一起没有任何意义.例如,您不能同时使用 reintroduceoverride 因为它们具有相反的含义.而且你不能同时使用 virtualoverride 因为一个暗示另一个.

So, then you just have to remember the purpose of each directive, and think about which ones don't make any sense together. For example, you cannot use reintroduce and override at the same time because they have opposite meanings. And you can't use virtual and override together because one implies the other.

如果你有很多指令堆积,你总是可以在你计算出你需要的其余指令时从图片中删除overload.给你的方法不同的名字,找出它们自己需要哪些other指令,然后添加overload,同时你再次给它们所有相同的名字.

If you have lots of directives piling up, you can always cut overload out of the picture while you work out the rest of the directives you need. Give your methods different names, figure out which of the other directives they need by themselves, and then add overload back while you give them all the same names again.

这篇关于Delphi:理解构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆