Delphi:了解构造函数
我想了解
>虚拟 当应用于对象构造函数。每次我随机添加关键字,直到编译器关闭 – 和(经过12年的Delphi开发)我宁愿知道我在做什么,而不是随机尝试。 给定假设的对象集合: 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; 我希望他们的行为方式很可能从声明,但是: > TComputer有简单的构造函数,后代可以覆盖它 现在代码不编译。我想明白为什么它不工作。我也想要理解覆盖构造函数的正确方法。或者你可以永远不重写构造函数?或者也许是完全可以接受的重写构造函数?也许你不应该有多个构造函数,也许它是完全可以接受的多个构造函数。 我想了解为什么。修复它将是显而易见的。 也可以看看 > Delphi: How to hide ancestor constructors? 编辑:我也希望得到一些推理的虚拟,覆盖,重载,重新引入的顺序。因为尝试所有关键字组合时,组合数量会爆炸: > virtual;超载; 编辑2:我想我们应该开始“是对象层次给定甚至可能?如果不是,为什么不呢?例如,它从根本上不正确有一个祖先的构造函数? 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?
现在这些声明不编译: //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关键字(我知道我不想重新引入,因为这将隐藏其他构造函数,我不想要): TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); virtual; overload; end; 但是失败:方法或属性后不允许字段定义。 我知道从经验,即使我没有一个方法或属性后的字段,如果我颠倒的虚拟和重载关键字的顺序:Delphi将关闭: TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); overload; virtual; end; 但我仍然得到的错误:
所以我尝试删除这两个关键字: TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); end; 但我仍然得到的错误:
所以我辞职,现在试着重新介绍: TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); reintroduce; end; 现在TCellPhone编译,但它已经使事情更糟的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关键字: TiPhone = class(TCellPhone) public constructor Create(Cup: Integer); constructor Create(Cup: Integer; Teapot: string); end; 但现在第二个创建说,它必须标记为重载,我做的(事实上,我会标记为超载,因为我知道如果我不会发生什么): TiPhone = class(TCellPhone) public constructor Create(Cup: Integer); overload; constructor Create(Cup: Integer; Teapot: string); overload; end; 所有的一切都在接口部分。不幸的是我的实现不会工作。我的单参数构造函数TiPhone不能调用继承的构造函数: constructor TiPhone.Create(Cup: Integer); begin inherited Create(Cup); <---- Not enough actual parameters end; 解决方法
我看到两个原因你原来的声明集不应该干净地编译:
>在TCellPhone中应该有一个警告,它的构造函数隐藏了基类的方法。这是因为基类方法是虚拟的,编译器担心你引入一个相同名称的新方法,而不覆盖基类方法。签名不同并不重要。如果你的意图确实是隐藏基类的方法,那么你需要使用reindroduce对后代声明,作为你的盲目猜测之一。该指令的唯一目的是平息警告;它对运行时行为没有影响。 忽略以后会发生什么TIPhone,下面的TCellPhone声明是你想要的。它隐藏了祖先方法,但你也希望它是虚拟的。它不会继承祖先方法的虚拟性,因为它们是两个完全独立的方法,它们恰好具有相同的名称。因此,你需要在新的声明上使用virtual。 TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); reintroduce; virtual; end; 基类构造函数TComputer.Create也隐藏了它的祖先TObject.Create的一个方法,但是因为TObject中的方法不是虚函数,编译器不会警告它。隐藏非虚拟方法总是发生,并且通常是不可见的。 由于您希望两个构造函数具有相同的名称,因此您需要使用overload指令。该指令需要用于所有原始声明 – 第一次每个不同的签名在后代中引入后续声明。我认为这是所有的声明(即使是基类),它不伤害这样做,但我想这不是必需的。所以,你的声明应该看起来像这样: 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告诉什么顺序应该进入:
这些是六个不同的类别,但在我的经验中,很少有超过三个任何声明。 (例如,需要调用约定的函数可能不是方法,所以它们不能是虚拟的。)我从来不记得顺序;我从来没有看到它记录到今天。相反,我认为记住每个指令的目的更有帮助。当你记得你需要不同任务的指令时,你最终只有两三个,然后很简单的实验得到一个有效的订单。编译器可能接受多个命令,但不要担心 – 命令在确定意义时不重要。编译器接受的任何顺序将具有与任何其他顺序相同的含义(除了调用约定;如果提到多个顺序,只有最后一个计数,所以不要这样做)。 所以,那么你只需要记住每个指令的目的,并考虑哪些不一起有意义。例如,您不能同时使用重新引入和覆盖,因为它们有相反的含义。你不能使用虚拟和覆盖在一起,因为一个暗示另一个。 如果你有很多指令堆积,你可以随时切出超出的图片,而你完成剩下的指令,你需要的。给你的方法不同的名字,找出他们自己需要的其他指令,然后添加重载,而你再次给他们所有相同的名称。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |