Understanding constructor visibility
Asked Answered
C

3

7

Here's two simple classes, initially both have no keywords (virtual, overload, override, reintroduce):

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

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

i will represent these above defintions as the slightly shorter:

TComputer = class(TObject)
   constructor Create(Teapot: Integer);

TCellPhone = class(TComputer)
   constructor Create(Teapot: Integer; Handle: string);

And when constructing TCellPhone there is only one constructor (int, string) - because the ancestor constructor has been hidden. i will indicate the visible constructors of TCellPhone as:

  • Teapot: Integer; Handle: string

Now for the question, the first 3 cases make sense, the 4th does not:

1. Ancestor constructor is hidden by descendant:

TComputer = class(TObject)
   constructor Create(Teapot: Integer);

TCellPhone = class(TComputer)
   constructor Create(Teapot: Integer; Handle: string);
  • Teapot: Integer; Handle: string

This makes sense, the ancestor constructor is hidden because i've declared a new constructor.

2. Ancestor virtual constructor is hidden by descendant:

TComputer = class(TObject)
   constructor Create(Teapot: Integer); virtual;

TCellPhone = class(TComputer)
   constructor Create(Teapot: Integer; Handle: string);
  • Teapot: Integer; Handle: string

This makes sense, the ancestor constructor is hidden because i've declared a new constructor.

Note: Because the ancestor is virtual: Delphi will warn you that you're hiding the virtual ancestor (in the previous example of hiding a static constructor: nobody cares, so no warning). The warning can be suppressed (meaning "Yeah yeah yeah, i'm hiding a virtual constructor. i meant to do that.") by adding reintroduce:

    TComputer = class(TObject)
       constructor Create(Teapot: Integer); virtual;

    TCellPhone = class(TComputer)
       constructor Create(Teapot: Integer; Handle: string); reintroduce;

3. Ancestor constructor not hidden in descendant because of overloading:

TComputer = class(TObject)
   constructor Create(Teapot: Integer);

TCellPhone = class(TComputer)
   constructor Create(Teapot: Integer; Handle: string); overload;
  • Teapot: Integer; Handle: string
  • Teapot: Integer

This makes sense, since the descendant constructor is an overload of the ancestor, so both are allowed to be present. The ancestor constructor is not being hidden.

4. Virtual ancestor constructor not hidden in descendant because overloading - but still get a warning:

This is the case that makes no sense:

TComputer = class(TObject)
   constructor Create(Teapot: Integer); virtual;

TCellPhone = class(TComputer)
   constructor Create(Teapot: Integer; Handle: string); overload;
  • Teapot: Integer; Handle: string
  • Teapot: Integer

    Method 'Create' hides virtual method of base type 'TComputer'

This makes little sense. Not only is the ancestor not hidden, the descendant is overloaded; it shouldn't even be complaining.

What gives?

Comeaux answered 8/10, 2010 at 17:44 Comment(3)
Edit: fixed copy-paste bug that gave the exact wrong results in case 1 and 2Comeaux
+1 Excellent write up. Agree with @Trinidad. Think it is a bug. Memory is nagging me that I have seen a report/post on this before, but fails to tell me when/where. If only I could travel back in time and tell myself to write me a note... :)Preternatural
Like I posted in your other question's, most likely a bug. When you start to play with corner case, you find quite a bit of funny behavior.Bowser
F
6

Delphi's documentation says:

If you overload a virtual method, use the reintroduce directive when you redeclare it in descendant classes. For example,

type
  T1 = class(TObject)
    procedure Test(I: Integer); overload; virtual;
  end;
  T2 = class(T1)
    procedure Test(S: string); reintroduce; overload;
  end;

Without the reintroduce directive, it still works, as you've noticed, but you'll get the warning.

Also, you are actually hiding TObject.Create, but it has nothing to do with the warning. If you think you might want access to TObject.Create also, do this:

type
  TComputer = class(TObject)
    constructor Create(Teapot: Integer); reintroduce; overload; virtual;
  end;

type
  TCellPhone = class(TComputer)
    constructor Create(Teapot: Integer; Handle: String); reintroduce; overload;
  end;
Falsehood answered 8/10, 2010 at 20:3 Comment(3)
i guess this is the correct answer. It's strange because you need to use reintroduce to suppress a warning about something being hidden that isn't being hidden. Looks like Borland chose to document it, in later versions of Delphi, rather than explain it.Comeaux
Just to complicate it more: the compiler doesn't actually warn on the first example if you omit reintroduce, regardless of what the help says. (Tested on Delphi 9)Hildie
The difference between the case here (in the Delphi documentation) and the one in the question is that T1.Test is defined as overload, unlike in the question. Somehow, overload suppresses the warning.Hildie
C
2

I would agree with Trinidad. The logic behind the warning probably only looks at whether the ancestor method is virtual/dynamic and whether a descendant method is marked as override or reintroduce.

This also applies to "normal" methods as well.

It can be suppressed by putting reintroduce before the overload modifier or by adding an overridden constructor to the descendant class that simply delegates to the ancestor constructor.

Christadelphian answered 8/10, 2010 at 19:39 Comment(0)
V
1

I've already noticed that. The warning is a bug, as far as I can tell, because the inherited method is not hidden. Should be reported at qc.embarcadero.com, if it isn't already.

Vertievertiginous answered 8/10, 2010 at 18:21 Comment(1)
The docs with my Delphi 5 don't include it, but looks like it's been documented since. So it's not a bug - it's a feature :)Comeaux

© 2022 - 2024 — McMap. All rights reserved.