Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi subclass constructor override

Tags:

delphi

I have the following classes declared in a single file:

type
 TCongruence = class(TObject)
  private
   a, b, n, numClass: integer;
   solutions, vals: TSolutions;
   hasSolutions: boolean;
   function divides(a, b: integer): boolean;
   function modulo(a, b: integer): integer;
  public
   constructor Create(a, b, n: integer); virtual;
   function getSolutions: TSolutions; virtual;
   function gcdExtended(p, q: integer): TSolutions;
   class function getGCD(u, v: integer): integer;
   property valA: integer read a;
   property valB: integer read b;
   property valN: integer read n;
   property getClass: integer read numClass;
   property hasSol: boolean read hasSolutions;
 end;

type
 TConguenceSystem = class(TCongruence)
  private
   system: array of TCongruence;
  public
   constructor Create(a: array of TCongruence); override;
   function getSolutions: integer; override;
 end;

The second one as you can see is a subclass because I need to use all the functions implemented in the TCongruence class. I have declared the constructor virtual so that I can call the override on the descendant.

Is that correct? Do I have to remove the virtual/override and simply use the constructor like this? (below)

constructor Create(a: array of TCongruence); 

I guess that in this case I am hiding the father's constructor. I have declared this constructor:

constructor TConguenceSystem.Create(a: array of TCongruence);
var i: integer;
begin

 SetLength(system, length(a)); // private var system: array of TCongruence
 for i := Low(a) to High(a) do
  begin
   system[i] := a[i];
  end;

 solutions := false;

end;
like image 834
Raffaele Rossi Avatar asked Mar 10 '23 07:03

Raffaele Rossi


1 Answers

When you intend to override behaviour of a method with the same signature in a descendent class then you must declare it virtual in the base class and the descendent class would then use override.

If, however, you wish to introduce a new method with a different signature then you must use the overload directive if you are declaring the method within the same class. This simply allows re-use of the same method name for what are, effectively, entirely different methods with different signatures. For example :

 TCongruence = class(TObject)
   public
     constructor Create(a : integer); overload;
     constructor Create(a, b, n: integer); overload;
 end;

If you are declaring a new method in a descendent class, however, with a different signature then none of these decorations are required.

 TCongruence = class(TObject)
   public
     constructor Create(a, b, n: integer);
 end;

 TCongruenceSystem = class(TCongruence)
   public
     constructor Create(a: array of TCongruence);
 end;

The above is fine - you are not overriding the original constructor, you are simply introducing a new one with a new signature. Since the latter belongs to a new class with a different name there is no ambiguity and overload is not required. You can even access the ancestor methods in the usual way here :

 TCongruence = class(TObject)
   private
     Fa, Fb, Fn : integer;
   public
     constructor Create(a, b, n: integer);
 end;

 TCongruenceSystem = class(TCongruence)
   private
     FArr : array of TCongruence;
   public
     constructor Create(a: array of TCongruence);
 end;

constructor TCongruence.Create(a, b, n: integer);
begin
  inherited Create;
  Fa := a;
  Fb := b;
  Fn := n;
end;

constructor TCongruenceSystem.Create(a: array of TCongruence);
var
  c : TCongruence;
  i : integer;
begin
  inherited Create(a[0].Fa, a[1].Fb, a[2].Fn);
  SetLength(FArr, Length(a));
  i := 0;
  for c in a do begin
    FArr[i] := c;
    Inc(i);
  end;
end;

Without the overload directive, however, the following would not be allowed :

var
  cs : TCongruenceSystem;
begin
  cs := TCongruenceSystem.Create(1, 2, 3); 
end.

since TCongruenceSystem is hiding the base class Create which takes three integer arguments. If you allow the overload, however :

 TCongruence = class(TObject)
   private
     Fa, Fb, Fn : integer;
   public
     constructor Create(a, b, n: integer); overload;
 end;

 TCongruenceSystem = class(TCongruence)
   private
     FArr : array of TCongruence;
   public
     constructor Create(a: array of TCongruence); overload;
 end;

Then the above call of cs := TCongruenceSystem.Create(1, 2, 3); would be allowed and the ancestor constructor would be used to build the descendent class.

These approaches can be combined, for example :

 TCongruence = class(TObject)
   public
     constructor Create(a : integer); overload; virtual; {overridable}
     constructor Create(a, b, n: integer); overload;     {only in base}
 end;

 TCongruenceSystem = class(TCongruence)
   public
     constructor Create(a:integer); overload; override;  {overrides parent}
     constructor Create(a: string); overload;            {introduce new}
 end;

In the case of the constructor you are introducing a method with a different set of parameters so this is allowed. In the case of getSolutions, however, the function is takes no parameters and differs only in the return type. An overloaded method needs to have a different parameter set, however, so this type of mutation is not allowed in a descendent class. getSolutions in the descendent class would need to take a different name if you intend it to also be a parameterless function with a different return type.

like image 117
J... Avatar answered Mar 30 '23 13:03

J...