You can
declare more than one routine in the same scope with the same name. This is
called overloading. Overloaded routines must be declared with the overload
directive and must have distinguishing parameter lists. For example, consider
the declarations
function
Divide(X, Y: Real): Real; overload;
begin
Result := X/Y;
end;
function
Divide(X, Y: Integer): Integer; overload;
begin
Result := X div Y;
end;
These declarations create two
functions, both called Divide, that take parameters of different types.
When you call Divide, the compiler determines which function to invoke
by looking at the actual parameters passed in the call. For example,
Divide(6.0, 3.0) calls the first Divide function, because its arguments
are real-valued.
You can
pass to an overloaded routine parameters that are not identical in type with
those in any of the routine’s declarations, but that are assignment-compatible
with the parameters in more than one declaration. This happens most frequently
when a routine is overloaded with different integer types or different real
types. For example,
procedure
Store(X: Longint); overload;
procedure
Store(X: Shortint); overload;
In these cases, when it is possible to do so without ambiguity, the compiler invokes the routine whose parameters are of the type with the smallest range that accommodates the actual parameters in the call. (Remember that real-valued constant expressions are always of type Extended.)
Overloaded routines must be
distinguished by the number of parameters they take or the types of their
parameters. Hence the following pair of declarations causes a compilation
error.
function
Cap(S: string): string; overload;
...
procedure
Cap(var Str: string); overload;
...
But the
declarations
function
Func(X: Real; Y: Integer): Real; overload;
...
function
Func(X: Integer; Y: Real): Real; overload;
...
are legal.
When an
overloaded routine is declared in a forward or interface declaration,
the defining declaration must repeat the routine’s parameter list.
The
compiler can distinguish between overloaded functions that contain
AnsiString/PChar and WideString/WideChar parameters in the same parameter
position. String constants or literals passed into such an overload situation
are translated into the native string or character type, which is
AnsiString/PChar.
procedure test(const S: string); overload;
procedure test(const W:
WideString); overload;
var
a: string;
b: Widestring;
begin
a := 'a';
b := 'b';
test(a); // calls String version
test(b); // calls WideString version
test('abc'); // calls String version
test(WideString('abc'));
// calls WideString version
end;
Variants
can also be used as parameters in overloaded function declarations. Variant is
considered more general than any simple type. Preference is always given to
exact type matches over variant matches. If a variant is passed into such an
overload situation, and an overload that takes a variant exists in that
parameter position, it is considered to be an exact match for the Variant type.
This can
cause some minor side effects with float types. Float types are matched by
size. If there is no exact match for the float variable passed to the overload
call but a variant parameter is available, the variant is taken over any
smaller float type.
For
example:
procedure foo(i: integer); overload;
procedure foo(d: double); overload;
procedure foo(v: variant); overload;
var
v: variant;
begin
foo(1); //
integer version
foo(v); //
variant version
foo(1.2); // variant version (float literals ->
extended precision)
end;
This example calls the variant version of foo, not the double version, because the 1.2 constant is implicitly an extended type and extended is not an exact match for double.
Extended is also
not an exact match for variant, but variant is considered a more general type (whereas
double is a smaller type than extended).
foo(Double(1.2));
This
typecast does not work. You should use typed consts instead.
const d: double = 1.2;
begin
foo(d);
end;
The above code works correctly, and
calls the double version.
const s: single = 1.2;
begin
foo(s);
end;
The above code also calls the
double version of foo. Single is a better fit to double than to variant.
When
declaring a set of overloaded routines, the best way to avoid float promotion
to variant is to declare a version of your overloaded function for each float
type (Single, Double, Extended) along with the variant version.
If you
use default parameters in overloaded routines, be careful of ambiguous
parameter signatures. For more information, see Default parameters
and overloaded routines.
You can
limit the potential effects of overloading by qualifying a routine’s name when
you call it. For example, Unit1.MyProcedure(X, Y) can call only routines
declared in Unit1; if no routine in Unit1 matches the name and parameter
list in the call, an error results.
For information about distributing overloaded methods in a class hierarchy, see Overloading methods. For information about exporting overloaded routines from a shared library, see The exports clause.
Forward and interface
declarations
可以在相同的作用域中以相同的名字声明多于一个例程,这叫做重载(overloading)。重载的例程必需以指示字overload声明并且参数列表必需有所区别。例如,考虑如下声明
function
Divide(X, Y: Real): Real; overload;
begin
Result := X/Y;
end;
function
Divide(X, Y: Integer): Integer; overload;
begin
Result := X div Y;
end;
上述声明创建了两个函数,都叫做Divide,接受不同类型的参数。调用Divide时,编译器根据实际传递给调用的参数来决定调用哪一个例程。例如,Divide(6.0, 3.0)调用第一个Divide函数,因为它的参数是实数值。
在所有的例程声明中,可以向重载的例程传递类型不等同的参数,但对参数赋值兼容且多于一个声明的重载例程除外。这通常出现在不同整数类型或不同实数类型的重载例程中。例如,
procedure
Store(X: Longint); overload;
procedure
Store(X: Shortint); overload;
在这些情况下,没有多义性的重载是可能的,编译器将调用参数类型与实际参数适应并且参数类型范围最小的例程。(实数值的常量表达式总是Extended类型。)
重载的例程必需在其接受的参数个数或参数类型上有所区别。因此,下面这一对声明会导致编译错误:
function
Cap(S: string): string; overload;
...
procedure
Cap(var Str: string); overload;
...
而下面的声明是合法的。
function
Func(X: Real; Y: Integer): Real; overload;
...
function
Func(X: Integer; Y: Real): Real; overload;
...
当一个重载的例程被声明为forward(向前声明)或接口声明时,其定义声明必需重申例程的参数列表。
编译器能够区分重载例程之间相同参数位置包含的AnsiString/PChar和WideString/WideChar参数。传递到此类重载情形的串常量或串文本被转换到本地的串或字符类型,即AnsiString/PChar。例如,
procedure test(const S: string); overload;
procedure test(const W:
WideString); overload;
var
a: string;
b: Widestring;
begin
a := 'a';
b := 'b';
test(a); //调用String版本
test(b); //调用WideString版本
test('abc'); //调用String版本
test(WideString('abc'));
//调用WideString版本
end;
变体也可以作为参数用于重载例程的声明。变体被认为比任何简单类型都通用。编译器总是优先选择精确匹配,而最后考虑变体匹配。如果一个变体传递到这样的重载情形,并且存在一个相应参数位置接受变体参数的重载例程,那么编译器将认为这是变体类型的精确匹配。
这可能对浮点类型导致一些细微的影响。浮点类型以尺寸匹配。对于传递到重载例程中的浮点变量,如果没有精确匹配,但存在可用的变体参数,那么传递的参数将被作为变体接受,而越过任何较小的浮点类型。
例如:
procedure foo(i: integer); overload;
procedure foo(d: double); overload;
procedure foo(v: variant); overload;
var
v: variant;
begin
foo(1); //integer版本
foo(v); //variant版本
foo(1.2); //variant版本(浮点数,作为extended类型解释)
end;
在上面的例子中,调用了例程foo的variant版本而没有调用double版本,是因为常量1.2暗指extended类型,该类型对于double不是精确匹配的。
Extended类型与变体也不是精确匹配,但变体被认为是更通用的类型(尽管double是一个比extended更小的类型)。
foo(Double(1.2));
上面的类型转换不会工作。需要用类型常量代替:
const d: double = 1.2;
begin
foo(d);
end;
上面的代码是正确的,并且调用了double版本。
const s: single = 1.2;
begin
foo(s);
end;
上面的代码也调用double版本,因为Single比变体更适合double。
声明一套重载的例程时,避免浮点参数作为变体接受的最佳途径是,与变体版本一起,为每个浮点类型(Single、Double、Extended)声明一个重载例程的版本。
如果在重载例程中使用缺省参数,那么需要注意不明确的参数标记。更多信息见缺省参数和重载例程。
调用一个例程时,可以通过限定例程名来限制潜在影响。例如,Unit1.MyProcedure(X, Y) 仅可以调用在单元Unit1中声明的例程;如果单元Unit1中没有例程名和参数列表都相匹配的例程,那么将导致错误。
有关类层次中分布重载方法的信息,见重载方法。有关从共享库中输出重载例程的信息,见exports子句。