I have reintroduced the form constructor in a base form, but if I override the original constructor in a descendant form, the reintroduced constructor is no longer visible.
type
TfrmA = class(TForm)
private
FWndParent: HWnd;
public
constructor Create(AOwner: TComponent; const AWndParent: Hwnd); reintroduce; overload; virtual;
end;
constructor TfrmA.Create(AOwner: TComponent; const AWndParent: Hwnd);
begin
FWndParent := AWndParent;
inherited Create(AOwner);
end;
type
TfrmB = class(TfrmA)
private
public
end;
type
TfrmC = class(TfrmB)
private
public
constructor Create(AOwner: TComponent); override;
end;
constructor TfrmC.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;
When creating:
frmA := TfrmA.Create(nil, 0);
frmB := TfrmB.Create(nil, 0);
frmC := TfrmC.Create(nil, 0); // Compiler error
My work-around is to override the reintroduced constructor or to declare the original constructor overloaded, but I’d like to understand the reason for this behavior.
type
TfrmA = class(TForm)
private
FWndParent: HWnd;
public
constructor Create(AOwner: TComponent); overload; override;
constructor Create(AOwner: TComponent; const AWndParent: Hwnd); reintroduce; overload; virtual;
end;
type
TfrmC = class(TfrmB)
private
public
constructor Create(AOwner: TComponent; const AWndParent: Hwnd); override;
end;
In your initial code your Create constructor hides the original Create. Which is why the compiler complains about TfrmC overriding a constructor it can no longer see from there.
You would normally get a compiler message about this, but “reintroduce” suppresses this. Whenever you need “reintroduce” to suppress compiler messages, alarm bells should go off as it is an indication that you are breaking polymorphism. Doesn’t mean you shouldn’t use it, but you should be aware of the implications.
In these cases, if I want to retain the ability to override the original constructor, I tend to add a different constructor, ie
and call the original constructor in its implementation. It does mean I need to call a different constructor, but as you already need to pass another parameter, that doesn’t seem like such a bad trade-off.
Edit:
As I mentioned in the comments, the compiler complains about tfrmC.Create(nil, 0); having too many parameters. It seems as though tfrmC.Create(AOwner) hides tfrmA.Create(AOwner, AWndParent); Thinking about it on the way home (traffic jams seem to have an advantage after all), there is an explanation for this.
1) The overload on the tfrmA.Create serves no direct purpose other than to allow introduction of other constructors with the same name.
2) The TfrmC constructor effectively hides the TfrmA constructor, reintroducing the signature that was hidden by the TfrmA constructor. In effect this isn’t an override of the original constructor but a re-introduction of a re-introduced one.
3) I feel the compiler should have emitted a warning about this hiding. The reason it doesn’t is possibly the overload directive on the TfrmA constructor. When you remove that, you get an error about the TfrmC.Create declaration differing from the previous one.
4) As the TfrmA constructor was hidden by TfrmC the compiler correctly complains about TFrmC.Create(nil, 0) having too many parameters.
What is described under 2) becomes obvious when you add the signature of the TfrmA constructor to TfrmC with just the override directive. This constructor is then immediately redlined by the IDE. The reason: two constructors with the same name and a missing overload directive for the TfrmC constructor that only has the AOwner parameter.
It also becomes clear when you comment out the second parameter in the instantiation of tfrmC and had only coded “inherited;” instead of “inherited Create(AOwner);” in its implementation. The compiler then complains about incompatible types.
The latter could have been solved by using either “inherited Create(AOwner);” (as you did) or “inherited Create(AOwner, 0);”. While the latter would have ensured a walk back up the inheritance tree, the former would have jumped straight back to TForm1.Create.
Using a slightly modified version of your code and provided TfrmC is instantiated with a single parameter, the result of “inherited Create(AOwner)” would have been:
alt text http://www.bjmsoftware.com/delphistuff/overloading/InheritedCreate1Param.jpg
while the result of “inherited Create(AOwner, 0);” would have been:
alt text http://www.bjmsoftware.com/delphistuff/overloading/InheritedCreate2Params.jpg
Adding overload to the TfrmC constructor as I suggested in the comments overrides the original TForm1.Create and allows for two constructors with the same name. This can be illustrated in the test project by turning on the TFRMC_OVERLOAD and INSTANTIATE2 conditional defines. This instantiates TfrmC with TfrmC.Create(nil, 0) and results in:
alt text http://www.bjmsoftware.com/delphistuff/overloading/InheritedCreate2ParamsOverload.jpg
Which shows TfrmB.Create being called for TfrmC as TfrmB is the first ancestor with an implementation for the two-parameter constructor. While instantiating TfrmC with TfrmC.Create(nil) (turn the INSTANTIATE2 conditional define back off), results in the same picture as the first or second (depending on the TWOPARAMS define).