Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Today Recently on Stackoverflow i learned that:

  • reintroduce is used to hide ancestor constructors
  • reintroduce is used to show ancestor constructors

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
like image 294
Ian Boyd Avatar asked Oct 06 '10 21:10

Ian Boyd


3 Answers

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.

like image 100
Rob Kennedy Avatar answered Sep 18 '22 11:09

Rob Kennedy


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.

like image 37
Sertac Akyuz Avatar answered Sep 18 '22 11:09

Sertac Akyuz


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)

like image 21
Ken Bourassa Avatar answered Sep 21 '22 11:09

Ken Bourassa