Delphi:理解构造函数 [英] Delphi: Understanding constructors
问题描述
我希望理解
- 虚拟
- 覆盖
- 过载
- 重新介绍
应用于对象构造函数时.每次我随机添加关键字直到编译器关闭 - 而且(在使用 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 itTCellPhone
has an alternate constructor, and descendants can override itTiPhone
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.
我还想对 virtual
、override
、overload
的顺序进行一些推理>, 重新引入
.因为在尝试所有关键字组合时,组合数量爆炸式增长:
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;
但是失败了:在方法或属性之后不允许定义字段
.
我从经验中知道,即使我在方法或属性之后没有字段,如果我颠倒 virtual
和 overload
关键字的顺序:德尔福会闭嘴:
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:
TCellPhone
中应该有一个警告,它的构造函数隐藏基类的方法.这是因为基类方法是虚拟,编译器担心您会在不覆盖基类方法的情况下引入具有相同名称的新方法.签名不同并不重要.如果您确实打算隐藏基类的方法,那么您需要在后代声明上使用reintroduce
,正如您的盲目猜测之一所示.该指令的唯一目的是平息警告;它对运行时行为没有影响.
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 usereintroduce
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是虚拟、动态或覆盖;调用约定是register、pascal、cdecl、stdcall或安全呼叫;并且警告是平台、弃用或库.
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).
所以,那么您只需要记住每个指令的目的,并考虑哪些指令放在一起没有任何意义.例如,您不能同时使用 reintroduce
和 override
因为它们具有相反的含义.而且你不能同时使用 virtual
和 override
因为一个暗示另一个.
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屋!