ADA支持对几乎它自身的所有运算符的重载,虽然ADA的运算符系统不像C/C++那样庞大灵活,ADA提供这种重载某种意义上说是为了达到作为对象操作的一致性要求,这也理应是运算符重载的目的。由于ADA本身的运算符定义特征,所有运算符都是单目或双目的,而ADA将他们均看成函数(function),其参数是输入性(in)对象类型,输出是这种对象类型。以下是一般用来阐释运算符重载的经典方法之一,复数的定义和运算。其实ADA本身也提供了复数的泛型模板,但是这里的小例子强调一些ADA的语言特征。比如ADA本身提供的复数泛型只支持数值类型作为复数分量,而这里采用了任何类型。
复数定义文件(complex.ads),这里几点注意:
ADA的泛型参数约束比较复杂,但是个人认为基本上是靠谱的,比PASCAL的类型相容性定义不知要好了多少了。因为它基本描述了ADA中允许的数据定义的种类。这里用了is private,大体上是支持几乎所有的ADA数据类型,关于这些泛型参数约束的说明可参见。由于类型约束很松,所以在泛型参数里还要加上对这个类型用到的运算和方法的指定。在这里是一组运算符(加减乘除和负号)以及一个转换到字符串的函数。这些运算符显然在复数运算中要用到。有意思的是,由于减号和负号的符号是一致的,作为泛型的形参的时候(见在后面complex.adb中实例化)会发生二义性(虽然理论上编译器可以通过函数参数来分辨这个重载,但可能ADA的设计不允许代码中出现这种含糊的情况),所以这里只能用实名函数来代替,随后再用ADA一个很有意思也很强大的renaming特性将他们转化成为运算符,供这个泛型模板的实现中继续用运算符的方式使用它们。这里可见在ADA中,在运算符重载意义上,运算符和函数是没有区别的。
generic -- the type of the components of the complex number is defined here -- as genereic argument delcared as 'private' which basically can -- be almost any ADA type type value_t is private; -- since value_t is declared as 'private', the caller needs to explicitly -- provide operators on the type used by this program even if the actual -- type is numerics. -- since subtract and minus operations share the same symbol, we might have -- to use named function for formal arguments here to differentiate them, -- fortunately we can use ADA's renaming feature to switch them back. with function "+" (x, y : value_t) return value_t; with function subtract (x, y : value_t) return value_t; with function "*" (x, y : value_t) return value_t; with function "/" (x, y : value_t) return value_t; with function minus(x : value_t) return value_t; -- function to convert the value to string with function to_string(x : value_t) return string; package complex is -- renaming for subtract and minus operations function "-"(x, y : value_t) return value_t renames subtract; function "-"(x: value_t) return value_t renames minus; -- declaration of the complex object type in the public region type object is tagged private; -- set individual component of a complex procedure set_real(obj : in out object; r : value_t); procedure set_imag(obj : in out object; i : value_t); -- get individual component of a complex function get_real(obj : in object) return value_t; function get_imag(obj : in object) return value_t; -- operator overloading for complex objects function "+"(obj1, obj2 : in object) return object; function "-"(obj1, obj2 : in object) return object; function "*"(obj1, obj2 : in object) return object; function "/"(obj1, obj2 : in object) return object; function "-"(obj : in object) return object; -- cast to and from string function to_string(obj : in object) return string; private -- detailed data definition for complex object type object is tagged record r : value_t; i : value_t; end record; end complex;
Note that I later realized that the operator as formal generic parameter can be specified in a different way (less ugly than having function-like names) to either
1. look for and make use of existing operators defined on the respective type without having to specify what and where they are (as long as the operator definitions end with box "is <>" which indicates use of default)
2. use => symbol to explicitly associate formal parameters and actual parameters excluding ambiguous formal parameters (e.g., "-" operators above, o.w., an error saying "name not allowed for overloaded operators" will be given)
3. pass the parameters in the same order as the corresponding formal parameters are defined.
复数的实现文件(complex.adb),从这里可见对分量类型的所有运算符都配置好了,所有只要用加加减减就能成了。with Ada.Strings.Fixed; use Ada.Strings.Fixed;package body complex is -- setters and getters procedure set_real(obj : in out object; r : value_t) is begin obj.r := r; end set_real; procedure set_imag(obj : in out object; i : value_t) is begin obj.i := i; end set_imag; function get_real(obj : in object) return value_t is begin return obj.r; end get_real; function get_imag(obj : in object) return value_t is begin return obj.i; end get_imag; -- operator overloading for complex objects function "+"(obj1, obj2 : in object) return object is result : object; begin result.r := obj1.r + obj2.r; result.i := obj1.i + obj2.i; return result; end "+"; function "-"(obj1, obj2 : in object) return object is result : object; begin result.r := obj1.r - obj2.r; result.i := obj1.i - obj2.i; return result; end "-"; function "*"(obj1, obj2 : in object) return object is result : object; begin result.r := obj1.r * obj2.r - obj1.i * obj2.i; result.i := obj1.r * obj2.i + obj1.i * obj2.r; return result; end "*"; function "/"(obj1, obj2 : in object) return object is q : value_t; result : object; begin q := obj2.r * obj2.r + obj2.i * obj2.i; result.r := (obj1.r * obj2.r + obj1.i * obj2.i) / q; result.i := (obj1.r * obj2.i - obj1.i * obj2.r) / q; return result; end "/"; function "-"(obj : in object) return object is result : object; begin result.r := -obj.r; result.i := -obj.i; return result; end "-"; -- conversion from complex object to the string that represents it function to_string(obj : in object) return string is result_str : string(1..32) := 32 * " "; begin result_str := overwrite(result_str, 1, to_string(obj.r) & to_string(obj.i) & 'j'); return result_str; end; end complex;
示例主程序(operatorsdemo.adb),在这里的泛型模板的实例化中要对这些形参赋予实际的内容,可见这里的运算符实参就是float运算符本身,(目前为止至少没有发现)而这些运算符估计是系统内建而不依赖于任何package的。
with Ada.Text_IO; use Ada.Text_IO;with Ada.Strings.Fixed; use Ada.Strings.Fixed;use Ada.Strings;with complex;procedure operatorsdemo is function float_to_string(value : float) return string is begin return Trim(float'Image(value), Both); end float_to_string; package float_complex is new complex(value_t=>float, "+"=>"+", subtract=>"-", minus=>"-", "*"=>"*", "/"=>"/", to_string=>float_to_string); use float_complex; c1, c2, c : float_complex.object; begin Put_Line("Operators Overloading Demo"); c1.set_real(3.0); c1.set_imag(4.0); c2.set_real(5.0); c2.set_imag(6.0); c := c1 * (-c2); Put("c = c1 * c2 = "); Put_Line(c.to_string); end operatorsdemo;