21

Consider the hypothetical object hierarchy, starting with:

TFruit = class(TObject)
public
    constructor Create(Color: TColor); virtual;
end;

and its descendant:

TApple = class(TFruit)
public
    constructor Create(); overload; virtual;
    constructor Create(Color: TColor); overload; override; //deprecated. Calls other constructor - maintaining the virtual constructor chain
end;

The idea here is that I've overridden the virtual constructor of the base class, with an overload that also happens to be virtual.

Delphi complains:

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

Except it doesn't hide it - it's right there!

  • I overrode the virtual method in the ancestor, and
  • I overloaded it with another version

What's the deal?

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • 1
    I get "Method 'Create' hides virtual method of base type 'TFruit'"; are you really getting "base type 'TApple'"? –  Feb 01 '12 at 21:09
  • @RBA Not exactly, the warning is incorrect in this case, although you can use `reintroduce;` to avoid it. –  Feb 01 '12 at 21:12
  • This is specific to Delphi 5? – EMBarbosa Feb 02 '12 at 00:33
  • 1
    @EMBarbosa No, but when using an older version, you might wonder if such a thing _is_ specific to that version. OP is just being informative. – NGLN Feb 02 '12 at 06:13
  • @NGLN Yup. So I think it doesn't need delphi-5 tag? – EMBarbosa Feb 02 '12 at 15:21
  • @EMBarbosa i *am* in Delphi 5. i don't know if newer versions of the language solve the problem by doing something completely different (e.g. linq closure delegate modads, with multiple interface inheritance and attributes). i include `delphi-5` so that the solution should be a fix in the language. And i don't know what pieces of information are relevant when i ask a question. Some people get grumpy when skip additional information. – Ian Boyd Feb 02 '12 at 18:59

2 Answers2

30

Two solutions:

type
  TFruit = class(TObject)
  public
    constructor Create(Color: TColor); virtual;
  end;

  TApple = class(TFruit)
  public
    constructor Create(); reintroduce; overload;
    constructor Create(Color: TColor); overload; override;
  end;

Or:

type
  TFruit = class(TObject)
  public
    constructor Create; overload; virtual; abstract;
    constructor Create(Color: TColor); overload; virtual;
  end;

  TApple = class(TFruit)
  public
    constructor Create(); override;
    constructor Create(Color: TColor); override; 
  end;
NGLN
  • 43,011
  • 8
  • 105
  • 200
  • 3
    +1. I couldn't find a combination of `overload`, `virtual`, and/or `override` that made XE2's compiler happy. :) – Ken White Feb 01 '12 at 21:21
  • @WarrenP That smell comes from other uses of `reintroduce` which often break inheritance. – NGLN Feb 02 '12 at 18:21
  • @NGLN In my case the compiler is fighting me when i'm trying to maintain inheritance. i could hide the ancestors and be done with it, but that would break polymorphic constructors. – Ian Boyd Feb 02 '12 at 19:01
  • Am I the only one who thinks Polymorphic constructors are another code smell? – Warren P Feb 02 '12 at 19:25
  • 2
    @WarrenP, `TComponent.Create` is a polymorphic constructor and it's central to the way Delphi works. I think it's beautiful, I don't smell anything. – Cosmin Prund Feb 03 '12 at 08:36
  • I'm willing to accept the limited use within the VCL (TComponent.Create), but when application codebases have used extensive polymorphic constructors, it has seemed like a code-smell to me. – Warren P Feb 03 '12 at 14:04
5

This appears to be a "which came first" sort of issue. (It appears NGLN found a solution.)

There's another solution, also. You can use a default parameter:

interface

type
  TFruit=class(TObject)
  public
    constructor Create(Color: TColor); virtual;
  end;

  TApple=class(TFruit)
  public
    constructor Create(Color: TColor = clRed); override;
  end;

implementation

{ TFruit }

constructor TFruit.Create(Color: TColor);
begin
  inherited Create;
end;

{ TApple }

constructor TApple.Create(Color: TColor);
begin
  inherited;
end;

// Test code
var
  AppleOne, AppleTwo: TApple;
begin
  AppleOne := TApple.Create;
  AppleTwo := TApple.Create(clGreen);
end;
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • 1
    +1 For creativity. But reintroduce isn't needed, and I would use `clDefault`. ;) – NGLN Feb 01 '12 at 21:27
  • Thanks. :) You're right. I think that's a remnant of one of the earlier trials, and the compiler didn't complain, so I didn't catch it. And I've never seen a `clDefault` colored apple. ;) – Ken White Feb 01 '12 at 21:31
  • I didn't mention it for coloring the apple, but to stick to the question's purpose of the added parameterless constructor in TApple. Clearly OP thinks there are apples without any color. ;) Besides, what color is clDefault anyway? It is the default color of an apple, isn't it? (Ok, it's implemented as black, but that's just because TColor simply requires some integer value). – NGLN Feb 01 '12 at 21:44
  • ;) I've never seen anything colored `clDefault` that I'd select as an apple I'd want to eat. It just seemed to me that, if you're creating a fruit called `Apple`, it should default to a typical color value for an apple (red, green, yellow or gold, etc.). :) I'd prefer not to see apples sit around until they turn black, but that may just be my own bias. – Ken White Feb 01 '12 at 21:48
  • Yeah, well, this is all a bit rhetoric, and the apple sample isn't making it a better case for me. But surely OP isn't designing fruit inheritance. The general point I am making is to make use of `clDefault` when speaking of _a default color_. Certainly a default parameter is somewhat meant to be _default_. ;) I make use of `clDefault` quite often: e.g. when setting a color property to `clDefault`, the color switches back to _a default state_ and could turn into any other color bút `clDefault` depending on other properties of the component. I'll stop now... ;) – NGLN Feb 01 '12 at 21:58
  • :) My last one: I specifically chose `clRed` as the default *because* the asker used `TApple` and `TFruit`, since it fit. In other circumstances, I'd probably use `clDefault`, unless there was an obviously better value to default to (such as `clMoneyGreen` for a `TUSPaperCurrency` class color property, or `clSkyBlue` for a `TSky` color). Now *I'll* stop. :) – Ken White Feb 02 '12 at 00:06