Delphi:了解构造函数

前端之家收集整理的这篇文章主要介绍了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有简单的构造函数,后代可以覆盖它
> TCellPhone有一个替代构造函数,后代可以覆盖它
> TiPhone覆盖两个构造函数调用每个构造函数的继承版本

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

我想了解为什么。修复它将是显而易见的。

也可以看看

> Delphi: How to hide ancestor constructors?
> Reintroducing functions in Delphi
> Delphi: How to add a different constructor to a descendant?

编辑:我也希望得到一些推理的虚拟,覆盖,重载,重新引入的顺序。因为尝试所有关键字组合时,组合数量会爆炸:

> virtual;超载;
> virtual;覆盖;
> override;超载;
> override;虚拟;
> virtual;覆盖;超载;
> virtual;超载;覆盖;
>过载;虚拟;覆盖;
> override;虚拟;超载;
> override;超载;虚拟;
>过载;覆盖;虚拟;
>等

编辑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?

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.

现在这些声明不编译:

//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;

但我仍然得到的错误

Method ‘Create’ hides virtual method of base type ‘TComputer’

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

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

但我仍然得到的错误

Method ‘Create’ hides virtual method of base type ‘TComputer’

所以我辞职,现在试着重新介绍:

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中的方法不是虚函数,编译器不会警告它。隐藏非虚拟方法总是发生,并且通常是不可见的。
>你应该在TIPhone中得到一个错误,因为不再有任何单参数构造函数要覆盖。你隐藏在TCellPhone。因为你想有两个构造函数,重新引入显然不是更好使用的正确选择。你不想隐藏基类构造函数;你想用另一个构造函数扩充它。

由于您希望两个构造函数具有相同的名称,因此您需要使用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告诉什么顺序应该进入:

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.

这些是六个不同的类别,但在我的经验中,很少有超过三个任何声明。 (例如,需要调用约定的函数可能不是方法,所以它们不能是虚拟的。)我从来不记得顺序;我从来没有看到它记录到今天。相反,我认为记住每个指令的目的更有帮助。当你记得你需要不同任务的指令时,你最终只有两三个,然后很简单的实验得到一个有效的订单。编译器可能接受多个命令,但不要担心 – 命令在确定意义时不重要。编译器接受的任何顺序将具有与任何其他顺序相同的含义(除了调用约定;如果提到多个顺序,只有最后一个计数,所以不要这样做)。

所以,那么你只需要记住每个指令的目的,并考虑哪些不一起有意义。例如,您不能同时使用重新引入和覆盖,因为它们有相反的含义。你不能使用虚拟和覆盖在一起,因为一个暗示另一个。

如果你有很多指令堆积,你可以随时切出超出的图片,而你完成剩下的指令,你需要的。给你的方法不同的名字,找出他们自己需要的其他指令,然后添加重载,而你再次给他们所有相同的名称

猜你在找的Delphi相关文章