Delphi: When does reintroduce hide ancestors and when does it show them?
Asked Answered
S

4

7

Today Recently on Stackoverflow i learned that:

i've been trying to make sense of it all, so here is a another, very specific question, supporting my main question dealing with constructors.


Update: replaced the entire question:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

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

When constructing TCellPhone, 3 constructors are avaible:

  • Cup: Integer
  • Cup: Integer; Teapot: string
  • [Teapot: String = '']

Question: Why is constructor(Teapot: string='') not being hidden?


Now i added a 3rd descendant:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

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

TiPhone = class(TCellPhone)
public
   constructor Create(Cup: Integer); override;
end;

When constructing TiPhone four constructors are available:

  • Cup: Integer
  • Cup: Integer
  • Cup: Integer; Teapot: string
  • [Teapot: string = '']

Why are there four constructors? i overrode one of the existing three. Edit: This may be a bug in code-insight, it shows me four - yet how could i possibly call then when two are the same.


Using the original code again:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

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

it's already known that TCellPhone has three constructors:

  • Cup: Integer
  • Cup: Integer; Teapot: string
  • [Teapot: String = '']

How do i alter the declaration of TCellPhone to hide the ancestor constructor? e.g. so that:

TNokia = class(TCellPhone)
end;

will only have two constructors:

  • Cup: Integer
  • Cup: Integer; Teapot: string

Now for the case where reintroduce is used to hide a non-virtual ancestor. In the previous case TiPhone has four constructors (ideally there would be only two - with TComputer somehow hiding its ancestor). But even if i can't fix TComputer, i can change TiPhone to only have the one:

TComputer = class(TObject)
public
    constructor Create(Teapot: string='');
end;

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

TiPhone = class(TCellPhone)
public
    constructor Create(Cup: Integer); reintroduce;
end;

Now TiPhone has only one constructor:

  • Cup: Integer

Reintroduce is normally only used to suppress the warning about hiding virtual ancestors. In this case:

Create(Teapot: string = '')

isn't virtual - yet i can still use reintroduce to hide it.


But now, if i add another overloaded to TiPhone:

TiPhone = class(TCellPhone)
public
   constructor Create(Cup: Integer); reintroduce; overload;
   constructor Create(Handle: String); overload;
end;

Then suddenly the (previously hidden) ancestors come back:

  • TiPhone.Create(7);
  • TiPhone.Create('pink');
  • TiPhone.Create(7, 'pink');
  • TiPhone.Create();

As you can see, i'm struggling to understand the logic of

  • when something is hidden
  • how to hide something
  • when something is shown
  • how to show something
Soriano answered 6/10, 2010 at 21:31 Comment(8)
@Ian: You really do ask a lot of questions today! Nice!Terce
Are you really saying you can compile run obj := TCellPhone.Create('foo')? I find that surprising.Bork
I have certainly run a full example using these definition. Luckily, we already have the answer below.Litta
@Andreas Rejbrand i know, i know. But when i ask "big-picture" questios, i don't get detailed answers. So i have to ask teey-tiny questions, and piece them together. i'm also at my wits end trying to makes sense of all this. Even Rob, two comments above, can't believe the code i presented works. At least i'm not alone in my confusion.Soriano
@Rob Kennedy i can amend the question with a screenshot, if you like?Soriano
No, Ian, I just wanted to make sure I was correctly interpreting what you said because the code here is not identical to [@Muhammad's example][1] — the base method isn't virtual, and there are multiple methods in the descendant, neither of which use reintroduce.Bork
@Rob Yes, it seems that very minor differences dramatically change the answer and understanding of this topic :/Soriano
#742235Urial
B
7

You don't use reintroduce to hide a method of an ancestor class. You do that simply by declaring a method with the same name as one in the ancestor class without overriding or overloading it. You use reintroduce to suppress the warning that Delphi raises when the ancestor class's method (the one being hidden) is virtual.

If the descendant's method overrides the ancestor's, then it's not hiding. Calls to the ancestor's method are routed to the descendant's instead.

If the descendant's method overloads the ancestor's, then it's also not hiding. Both are available to call.

Bork answered 6/10, 2010 at 21:58 Comment(4)
There is the special case when overloading a virtual method. You get to use the combined reintroduce; overload;. This seems to not fit in well with your explanation, as a bare overload does produce the warning.Litta
wrt what Muhammad said, can you expand your answer, Rob, to explain what's going on in that case (the case where adding reintroduce is used to hide a method of an ancestor class)?Soriano
I'm afraid I can't, @Ian. This will require me to experiment, and I don't have a Delphi compiler available to do that. Somebody else will have to enumerate all the different ways these directives can be used together, and what effects the have (or don't have).Bork
i'll expand the question where reintroduce is hiding a non-virtual ancestor method (or perhaps that's a side effect of it suppressing a warning)Soriano
C
1

You cannot override a method that's not virtual, so you're not hiding anything. That's why there's no warning.

edit: I'd withdraw my assertion "you're not hiding anything". I think I don't quite understand the meaning of hiding here. I've asked a quesiton on this.

update:
Based on the answer I got, I'd like to re-phrase my answer: Since TComputer.Constructor is not declared virtual, you've already hidden that method from descendant classes. So, TCellPhone constructors cannot hide what has not been visible at all, hence no compiler warning.

Countermeasure answered 6/10, 2010 at 21:58 Comment(7)
That is the right answer. Specifically, the constructor of TComputer should be virtual to trigger the warnings.Litta
But Sertac, what Ian's saying is that TComputer.Create isn't hidden in this case. He can call TCellPhone.Create('foo'), and the code compiles and runs, creating a TCellPhone object. He offered to provide a screen shot to prove it.Bork
@Rob - I asked a question about what compiler meant with hiding. It's not about not being able to call at all, it means you won't be able to override TComputer.Constructor in descendants of TCellPhone. Please see Ken's answer and Barry's comment.Countermeasure
@Sertac Akyuz You can't override TComputer.Constructor because it's not virtual - not because it's "hidden".Soriano
@Ian - It's hidden because it's not virtual. Or to restate: If it's not virtual then it's hidden since you cannot override it. Well, at least that's how I understand it..Countermeasure
@Sertac But it's not hidden and it's not virtual.Soriano
@Ian - It all really comes down to what hidden means. If it means "you won't be able to access TComputer's constructor" you're absolutely correct. If it means however, "you won't be able to override TComputer's constructor from TCellPhone's descendants" I should be right. If it means yet something else,.. who knows!Countermeasure
M
1

Well, it seems you can't hide a method/constructor in a class where you overload it as well. I came with this tiny "hack" to manage to hide the constructor from TComputer

  TComputer = class(TObject)
  public
      constructor Create(Teapot: string='');
  end;

  THackComputer = class(TComputer)
  public
    constructor Create(Cup : Integer);virtual;
  end;

  TCellPhone = class(THackComputer)
  public
      constructor Create(Cup: Integer); overload; override;
      constructor Create(Cup: Integer; Teapot: string); overload; virtual;
  end;

  TiPhone = class(TCellPhone)
  public
    constructor Create(Cup: Integer); reintroduce; virtual;
  end;

In that exemple, TiPhone will only have 1 constructor available. It does break the polymorphism though (a price to pay to hide the 2nd constructor from TCellPhone). I would like to know if anyone has found a way to do so without breaking the polymorphism.

Also, take note that it's not because the code insight show you 4 "constructors" that there is indeed 4 available. I noted that, for each "override" of the contructor, I would have 1 constructor listed in the code insight. But only the descendant constructor will be called in that situation.

This exemple will complain that the 2nd constructor of TCellPhone hide the one from THackComputer, but I think it's a false positive as the one from THackComputer is overriden in TCellPhone. (Corner case bug I guess, as this is not a very common code strucure)

Mayhap answered 7/10, 2010 at 17:27 Comment(2)
That's a very neat trick. i do get the same issue you do (complaining that the other constructor in TCellPhone hides the one from THackComputer). Explaining why it complains is something that is worthy of being asked - in a fifth question on constructors in Delphi. Reason being is because i don't understand why it is complaining, and that's always an opportunity to learn.Soriano
Well, like I said, it might be just a corner case bug. Some code structure fool the compiler in giving "False positive" warnings once in a while.Mayhap
S
0

I wanted to put this as comment to Rob Kennedy's answer, but since I can't, here I go..

All the while there is no warning about hiding ancestor constructors.

Simply because you don't.

If i'm hiding the ancestor, why is there no warning? i have no reintroduce.

Again, simply because you don't hide anything.

You saw the proof that you did not hide anything. It's the 3 available constructors that you've inspected is the proof.

Why can reintroduce be used to hide the ancestor?

Like Rob mentioned, reintroduce is merely suppressing the compiler hint/warning. There is no real technicality behind that word. Thus you don't hide anything with reintroduce.

I would like to put my thought on how to hiding, but I agree with Sertac, first I have to know what is your definition of hiding in this case.

Edit: I just read the posts you mentioned, I think you have misunderstood the concepts. Here my short explanation.

reintroduce is used to hide ancestor constructors

The answer of that post does not indicate that. The one that really hiding ancestor's constructor is the NEW CONSTRUCTOR of the descendant with the same parameter with the ancestor's. The keyword reintroduce is simply suppressing compiler warning.

reintroduce is used to show ancestor constructors

In the answer of that post, it's the OVERLOAD keyword that makes ancestor's constructor still available.

ADDITION in response to Ian's question in his comment below:

The first step to solve your confusion is to identify the real problems. If we carefully examine you posts, it will become obvious that you actually wanted to solve two problems in one step. The two problems are:

  1. To hide ancestor constructor with specific name
  2. To have multiple constructor with same specific name in the descendant.

Although they may seem simple problems, but careful inspection will immediately struck your head that their natures are exactly opposite to each other. Problem 1 wants to hide a method/constructor while problem 2 wants to show not only one but multiple method/constructor. So if you mix them together in one step, they will definitely cancel each others out. No wonder they give you headache... :)

The basic rule to solve these two problems is not to mix them in one step. This means we need an intermediate class to solve problem 1, and do the overloading in descendants of that intermediate class. Something like this:

type
  TComputer = class(TObject)
  private
    FCaller: TConstructorCaller;
  public
     constructor Create(Teapot: string=''); virtual;

     property Caller: TConstructorCaller read FCaller;
  end;

  TBaseCellphone=class(TComputer)
    constructor Create(Cup: Integer); virtual;
  end;

  TCellPhone = class(TBaseCellphone)
  protected
  public
    constructor Create(Cup: Integer); overload; override;
    constructor Create(Cup: Integer; Teapot: string); overload; virtual;
  end;

  TiPhone = class(TCellPhone)
  public
    constructor Create(Cup: Integer); reintroduce; overload;
    constructor Create(Handle: String); reintroduce; overload;
  end;

From the above code, TBaseCellphone is the intermediary class. In this scenario its task is solely to hide the constructor Create of TComputer. Please note that you MUST NOT use overload keyword here, or the hiding will be canceled. Now after the hiding is done, now you can freely spam your overload keyword in its descendants to get multiple constructor with same name.

To check, you can see that the following code will not compile:

   TCellPhone.Create('My Teapot');
Softener answered 7/10, 2010 at 9:30 Comment(3)
i think i've found the bug in Delphi. The overload keyword is making the ancestor's constructor still available - when that is not what it's meant for. It's meant to allow two overloads in TCellPhone. How do i use overload to allow multiple overloads, but not allow visibility of methods from the ancestor? IOW: How do i hide an ancestor constructor?Soriano
@Ian - That wouldn't be a bug. Overloading Methods: "... if the redeclared method has a different parameter signature from its ancestor, it overloads the inherited method without hiding it."Countermeasure
Ian, overload allows you to have multiple routines or methods with the same name. They only need to have different parameters. Regarding your question, I'll see if I can come up with good example.Softener

© 2022 - 2024 — McMap. All rights reserved.