Overloading in Pascal, with or without the "overload" keyword? - michaliskambi/modern-pascal-introduction GitHub Wiki
Both Delphi and FPC allow to overload with the overload
keyword.
FPC allows to also overload things without using the overload
keyword.
Their practical end-result is similar, though not exactly equal. That is, you want to enable multiple ways how your routine (method or global function / procedure). So you just write this:
type
TMyClass = class
procedure DoSomething(const I: Integer); overload;
procedure DoSomething(const S: String); overload;
end;
procedure TMyClass.DoSomething(const I: Integer);
begin
Writeln('Got int', I);
end;
procedure TMyClass.DoSomething(const S: String);
begin
Writeln('Got string', S);
end;
var
O: TMyClass;
begin
O := TMyClass.Create;
try
O.DoSomething(123);
O.DoSomething('hello');
finally FreeAndNil(O) end;
end.
In FPC, you can remove overload
above, and it will work the same.
However, the difference between these 2 overloading ways in FPC is actually more impactful.
Technically speaking,
-
Overloading with
overload
means you add a new way of calling given routine to the namespace. This is added to how the routine could be called according to ancestor classes (in case of methods) or other units (in case of global functions / procedures). -
Overloading without
overload
means that you introduce a collection of overloads in this class or unit. Other code can call any of the overloads, but they "obscure" overloads in ancestor classes (in case of methods) or other units (in case of global functions / procedures).
You can say the first way of overloading (with overload
) is cross-class and cross-unit. The 2nd way (without overload
) is not cross-class and cross-unit.
As an example when cross-unit overloading makes sense: You can have a unit 'CastleMath` that defines
function Lerp(const A, B: Single; const T: Single): Single; overload;
function Lerp(const A, B: Double; const T: Single): Double; overload;
And another unit CastleVectors
that defines
function Lerp(const A, B: TVector2; const T: Single): TVector2; overload;
function Lerp(const A, B: TVector3; const T: Single): TVector3; overload;
And if your code uses both CastleMath, CastleVectors
than all 4 Lerp
overloads are available. It's great in this case.
There are however cases when this cross-class and cross-unit is not desirable. The most prominent is an issue when overloading constructors with overload
, and thus accidentally allowing user to call a parameterless constructor of the ancestor. Consider this:
// THIS CODE HAS A TRAP, DON'T DO THIS!
type
TMyClass = class
constructor Create;
end;
TMyClassDescendant = class(TMyClass);
constructor Create(const I: Integer); overload;
constructor Create(const S: String); overload;
end;
var
D: TMyClassDescendant;
begin
// This *compiles* but it's a trap...
// You created TMyClassDescendant instance, without calling code of any TMyClassDescendant constructor.
// Only TMyClass.Create got called, the parameterless constructor.
// Actually, this example would also make sense without TMyClass, because TObject itself has parameterless constructor.
D := TMyClassDescendant.Create;
D.Free;
end;
See for more info:
- https://stackoverflow.com/questions/14003153/how-to-hide-the-inherited-tobject-constructor-while-the-class-has-overloaded-one
- http://andy.jgknet.de/blog/2011/07/hiding-the-tobject-create-constructor/
- comments at CGE TNoParameterlessContructor:
type
{ Descend from this to hide a parameterless constructor.
In Delphi, if you have multiple overloaded constructors (marked
with "overload"), the user can call any constructor of the ancestor
class that you have not "obscured" by a new definition.
In particular, if all your constructors have some parameters,
then user can "bypass" them by calling a parameterless constructor
defined by TObject.
Descend from this class to disallow this.
See
https://stackoverflow.com/questions/14003153/how-to-hide-the-inherited-tobject-constructor-while-the-class-has-overloaded-one
http://andy.jgknet.de/blog/2011/07/hiding-the-tobject-create-constructor/
Note that in FPC (in ObjFpc mode) this is not a problem,
as the standard overloading (without the "overload" keyword) is more sane:
the overloaded constructors obscure the ancestor constructors. }
{$warnings off}
TNoParameterlessContructor = class
strict private
constructor Create;
end;
{$warnings on}