Delphi:了解构造函数 [英] Delphi: Understanding constructors
问题描述
我期待
- 虚拟
- 覆盖
- 重载
- 重新导入
假定一组对象:
TComputer = class(TObject)
public
constructor Create(Cup:Integer);虚拟;
end;
TCellPhone = class(TComputer)
public
构造函数Create(Cup:Integer; Teapot:string);虚拟;
end;
TiPhone = class(TCellPhone)
public
构造函数Create(Cup:Integer);覆盖;
constructor Create(Cup:Integer; Teapot:string);覆盖;
end;
我希望他们的行为方式可能从声明中显而易见,但是:
-
TComputer
有简单的构造函数,后代可以覆盖它 -
TCellPhone
有一个替代构造函数,后代可以覆盖它 -
TiPhone
覆盖两个构造函数,调用每个构造函数的继承版本
现在代码不能编译。我想了解为什么它不工作。我也想要理解覆盖构造函数的正确方法。或者你可以永远不重写构造函数?或者也许是完全可以接受的重写构造函数?也许你不应该有多个构造函数,也许这是完全可以接受的有多个构造函数。
我想了解为什么。
另请参阅
- Delphi:如何隐藏祖先构造函数?
- < a href =http://stackoverflow.com/questions/53806/reintroducing-functions-in-delphi>在Delphi中重新引入功能
- Delphi:如何向后代添加不同的构造函数?
-
编辑:我还希望在 virtual
,覆盖
,重载
,重新导入
。因为尝试所有关键字组合时,组合数量会爆炸:
- virtual;过载;
- virtual;覆盖;
- 覆盖;重载;
- 覆盖; virtual;
- virtual;覆盖;过载;
- virtual;超载;覆盖;
- overload;虚拟;覆盖;
- 覆盖;虚拟;重载;
- 覆盖;超载; virtual;
- overload;覆盖;虚拟;
- 等
应该以是给定的对象层次结构可能吗?如果不是,为什么不?例如,从祖先有一个构造函数从根本上不正确?
TComputer = class(TObject)
public
constructor Create(Cup:Integer);虚拟;
end;
TCellPhone = class(TComputer)
public
构造函数Create(Cup:Integer; Teapot:string);虚拟;
end;
我希望 TCellPhone
构造函数。但我不能在Delphi中找到关键字的组合,使它认为这是一个有效的事情。我认为我可以有两个构造函数在这里 TCellPhone
?
注意:此行下面的所有内容不是严格需要回答
问题 - 但它有助于解释我的想法。也许你可以看到,
基于我的思维过程,什么
基本部分我错过了
使一切清楚。
现在这些声明不编译:
//方法创建隐藏基类型的虚方法TComputer:
TCellPhone = class(TComputer)
constructor Create(Cup:Integer; Teapot:string);虚拟;
//方法创建基类型的隐藏虚方法TCellPhone:
TiPhone = class(TCellPhone)
public
constructor Create(Cup:Integer);覆盖;
constructor Create(Cup:Integer; Teapot:string);超载; < --------
end;
首先,我将尝试修复 TCellPhone
。我将从随机添加重载
关键字开始(我知道我不想要重新引入
,因为这将隐藏其他构造函数,我不想):
TCellPhone = class(TComputer)
public
构造函数Create(Cup:Integer; Teapot:string);虚拟;超载;
end;
但是失败:方法或属性后不允许字段定义
。
我从经验中得知,即使我在方法或属性后面没有字段,如果我颠倒了
virtual
和 overload
关键字:Delphi会关闭: TCellPhone = class(TComputer)
public
构造函数Create(Cup:Integer; Teapot:string);超载;虚拟;
end;
但我仍然得到错误:
方法'Create'隐藏基本类型'TComputer'的虚拟方法
:
TCellPhone = class(TComputer)
public
构造函数创建(Cup:Integer; Teapot:string );
end;
但我仍然得到错误:
方法'Create'隐藏基本类型'TComputer'的虚拟方法
尝试重新导入
:
TCellPhone = class(TComputer)
public
constructor Create(Cup:Integer; Teapot:string);再引入
end;
现在TCellPhone编译,但它使TiPhone变得更糟:
TiPhone = class(TCellPhone)
public
constructor Create(Cup:Integer);覆盖; < -----不能覆盖静态方法
构造函数Create(Cup:Integer; Teapot:string);覆盖; < -----不能覆盖静态方法
end;
两个都抱怨我不能覆盖它们,所以我删除 override
关键字:
TiPhone = class(TCellPhone)
public
构造函数Create Cup:Integer);
constructor Create(Cup:Integer; Teapot:string);
end;
但现在第二个创建说它必须标记为重载,我做的(事实上,因为我知道如果我不会发生什么会发生):
TiPhone = class(TCellPhone)
public
constructor Create(Cup:Integer);超载;
constructor Create(Cup:Integer; Teapot:string);超载;
end;
接口
。不幸的是我的实现不会工作。我的单参数构造函数TiPhone不能调用继承的构造函数:
构造函数TiPhone.Create(Cup:Integer);
begin
inherited创建(Cup); < ----没有足够的实际参数
end;
我看到两个原因, t compile cleanly:
-
TCellPhone / code>,它的构造函数隐藏基类的方法。这是因为基类方法是 virtual ,并且编译器担心您引入的是同名的新方法,而不会覆盖基类方法。签名不同不重要。如果你的意图确实隐藏了基类的方法,那么你需要在后代声明中使用
reintroduce
,作为你的盲目猜测之一。该指令的唯一目的是平息警告;
稍后忽略将在
TIPhone
中发生的情况,以下内容TCellPhone
声明是你想要的。它隐藏了祖先方法,但你也希望它是虚拟的。它不会继承祖先方法的虚拟性,因为它们是两个完全独立的方法,它们恰好具有相同的名称。因此,您还需要在新声明中使用virtual
。TCellPhone = class(TComputer)
public
构造函数Create(Cup:Integer; Teapot:string);再引入虚拟;
end;
基类构造函数
TComputer.Create
,也隐藏了其祖先TObject.Create
的方法,但由于TObject
不是虚拟的,编译器不会警告它。 -
您应该在
错误 > TIPhone
,因为不再有任何单参数构造函数要覆盖。你隐藏在TCellPhone
。由于您想要有两个构造函数,重新引入
清楚地不是是早期使用的正确选择。你不想隐藏基类构造函数;
由于您希望两个构造函数具有相同的名称,因此您需要使用
overload
指令。该指令需要用于所有原始声明 - 第一次引入每个不同的签名后代中的后续声明。我认为这是在所有声明(即使是基类),这不是伤害,但我想这不是必需的。所以,你的声明应该是这样:TComputer = class(TObject)
public
constructor Create (Cup:Integer);
overload; //允许后代添加更多名为Create的构造函数。
virtual; //允许后代重新实现此构造函数。
end;
TCellPhone = class(TComputer)
public
构造函数Create(Cup:Integer; Teapot:string);
overload; //添加另一个名为Create的方法。
virtual; //允许后代重新实现此构造函数。
end;
TiPhone = class(TCellPhone)
public
构造函数Create(Cup:Integer);
override; //重新实现祖先的Create(Integer)。
constructor Create(Cup:Integer; Teapot:string);
override; //重新实现祖先的Create(Integer,string)。
end;
现代文档告诉我们应该执行什么命令:
reintroduce ; overload ; 绑定; 调用约定; 抽象; 的 动态 ,或替换; 注册, pascal , cdecl , stdcall 或 safecall ; 或库。
这是六个不同的类别,但在我的经验中,很少有三个以上的声明。 (例如,需要调用约定的函数可能不是方法,所以它们不能是虚拟的。)我从来不记得顺序;我从来没有看到它记录到今天。相反,我认为记住每个指令的目的更有帮助。 当你记得你需要用于不同任务的指令时,你最终只能有两三个,然后很容易实验才能得到有效的顺序。编译器可能接受多个命令,但不要担心 - 命令在确定意义上不重要。编译器接受的任何顺序都有相同的意义(除了调用约定;如果你提到多个,只有最后一个计数,所以不要这样做)。
所以,你只需要记住每个指令的目的,并考虑哪些指令没有任何意义。例如,不能同时使用 reintroduce
和 override
,因为它们具有相反的含义。您不能同时使用 virtual
和 override
。
如果你有很多的指令堆积,你总是可以剪下 overload
出来的图片,而你完成剩下的指令,你需要的。给你的方法不同的名字,找出他们自己需要的其他指令,然后添加 overload
回来,而你给他们
i'm looking to understand
- virtual
- override
- overload
- reintroduce
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.
Given a hypothetical set of objects:
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
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.
See also
- Delphi: How to hide ancestor constructors?
- Reintroducing functions in Delphi
- Delphi: How to add a different constructor to a descendant?
Edit: 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:
- virtual; overload;
- virtual; override;
- override; overload;
- override; virtual;
- virtual; override; overload;
- virtual; overload; override;
- overload; virtual; override;
- override; virtual; overload;
- override; overload; virtual;
- overload; override; virtual;
- etc
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;
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;
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;
But that fails: Field definition not allowed after methods or properties
.
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;
But i still get the error:
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;
But i still get the error:
Method 'Create' hides virtual method of base type 'TComputer'
So i resign myself to now trying reintroduce
:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); reintroduce;
end;
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;
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;
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:
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.Ignoring what's going to happen with
TIPhone
later on, the followingTCellPhone
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 usevirtual
on the new declaration as well.TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); reintroduce; virtual; end;
The base-class constructor,
TComputer.Create
, is also hiding a method of its ancestor,TObject.Create
, but since the method inTObject
is not virtual, the compiler doesn't warn about it. Hiding non-virtual methods happens all the time and is generally unremarkable.You should get an error in
TIPhone
because there is no longer any one-argument constructor to override. You hid it inTCellPhone
. 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.Since you want both constructors to have the same name, you need to use the
overload
directive. That directive needs to be used onall the original declarations — the first time each distinct signature is introducedsubsequent 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:
reintroduce; overload; binding; calling convention; abstract; warning
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).
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.
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屋!