ADA 95教程 高级特性 通用子程序
通用单元增加了ada的灵活性
包和通用单元的概念使Ada能够用于通用软件组件。实用程序例程可以编写一次,并在多个程序中使用,从而消除了反复重写和调试实用程序的需要。如果没有Ada的通用能力,这种能力将会大大减弱。
什么是通用单元?
Example program ------> e_c30_p1.ada
-- Chapter 30 - Program 1 generic type ITEM is range <>; -- integer class only procedure Exchange_Data(X,Y : in out ITEM); procedure Exchange_Data(X,Y : in out ITEM) is Temp : ITEM; begin Temp := X; X := Y; Y := Temp; end Exchange_Data; -- This is the beginning of the main program which uses the generic -- procedure defined above. with Exchange_Data; with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure SwapSome is type MY_INT is new INTEGER range 1..128; procedure SwapInt is new Exchange_Data(INTEGER); procedure SwapNew is new Exchange_Data(MY_INT); Index1 : INTEGER := 17; Index2 : INTEGER := 33; Limit1 : MY_INT := 3; Limit2 : MY_INT := 7; begin SwapInt(Index1, Index2); SwapNew(Limit1, Limit2); Put(Index1, 5); Put(Index2, 5); New_Line; Put(INTEGER(Limit1), 5); Put(INTEGER(Limit2), 5); New_Line(2); SwapInt(Index1, INTEGER(Limit1)); SwapNew(MY_INT(Index2), Limit2); Put(Index1, 5); Put(Index2, 5); New_Line; Put(INTEGER(Limit1), 5); Put(INTEGER(Limit2), 5); New_Line(2); end SwapSome; -- Result of Execution -- 33 17 -- 7 3 -- -- 7 3 -- 33 17
通用单元是子程序或包的模板,但不能直接执行。通用单元的一个副本可以被实例化,并且产生的单元可以像任何其他子程序或包一样被执行。与本课程中的所有其他主题一样,学习如何使用这种新技术的最好方法是通过使用一个示例,所以请检查名为e_c30_p1.ada的程序。
一个简单的泛型过程
泛型规范在第2行到第4行给出,泛型主体在第6行到第12行给出。仔细检查这个过程会发现,没有为列为ITEM类型的类型定义实际类型。使用泛型过程的目的是允许您在合理的范围内将过程用于任何想要的类型,而不必为每个特定类型重写过程。为了在程序中使用该过程,我们将提供一个类型,当实例化过程的副本时,该类型将在过程中出现ITEM类型的每个位置被替换。
保留字generic用于标记一个通用单元的开始,它可以是一个包、一个过程或一个函数。在保留词(在本例中是泛型和过程)之间,我们包含了一个正式泛型参数列表,这些参数定义了可选类型、变量和将在过程体中使用的其他实体。在这种情况下,只有一个名为ITEM的正式泛型参数,并且它被限制为类型的整数类的任何类型。很快将给出一个解释来定义为什么ITEM的类型被限制为类型的整数类。就目前而言,简单地接受这个说法是正确的。
第4行中的过程规范和第6到12行中的过程主体与我们在本教程中使用的其他任何过程没有什么不同,只是名称为ITEM的类型在整个过程中是未定义的。因为类型ITEM是未定义的,所以该过程在当前形式下是不可用的。
我们如何使用泛型过程?
为了使用通用过程,我们必须首先告诉系统获取它的一个副本,我们在第18行使用with子句做到了这一点。
引用主程序,在第24行声明名为MY_INT的整数类的派生类型。在第26行中,我们声明了一个名为SwapInt的过程,由于在声明之后使用了保留字new,所以它定义了名为Exchange_Data的泛型包的实例化,以便与INTEGER类型一起使用。您可能还记得,实例化过程意味着创建泛型过程的实例,在本例中,我们定义了一个可以交换INTEGER类型变量的过程。
每次在原始的泛型过程中使用泛型词ITEM时,在产生的可执行过程中使用类型INTEGER。结果将与复制泛型过程完全相同,将其名称更改为SwapInt,然后将单词ITEM的每次出现替换为类型INTEGER。使用任意两个INTEGER类型变量调用SwapInt将导致交换它们的值。
这对我们有什么好处?
诚然,我们可以在一开始就简单地定义过程SwapInt,一切都会很好。真正的好处来自程序的下一行,我们实例化了名为Exchange_Data的泛型过程的另一个副本,将其命名为SwapNew,并告诉系统使用MY_INT类型替换名为ITEM的正式泛型类型。因此,第27行相当于为前面声明的新类型再次写出泛型过程。
如果我们有大量不同类型的整型变量,我们可以为每个变量实例化这个过程的一个副本,每个变量只对应一行,因此好处是非常显著的。
可重用软件
一旦这个过程被编写和调试,它就可以在任意数量的程序中使用,因为它不需要为我们组成的每个新类型进行修改。一旦过程经过彻底测试,不同的程序员可以在不同的包中使用它,因此每个程序员不必重新编写它。在软件社区中有一个新的行业正在发展。这个行业专门用Ada编写可重用的软件组件,这些组件是用通用单元提供的灵活性编写的。
程序的其余部分应该不会对您的理解造成任何问题,因此您可以自行研究它,然后编译和执行它。
关于一般单位的一些注意事项
这个程序可以分为两个单独的文件,第一个文件包括第1行到第12行中的泛型过程,第二个文件包括第16行到第56行中的使用程序。这些文件可以单独编译,并且一旦编译了泛型过程,就不再需要再次编译它,而是可以以编译后的形式包含在Ada库中。为了方便起见,本文将通用过程和主程序结合在一起。
关于编译单元的另一个注意事项
在某些情况下,通用部分可以再次拆分为两个单独的文件,第一个是第2行到第4行中的通用规范,另一个是第6行到第12行中的过程主体。如果它们以这种方式分割,则必须保持编译的顺序,以便能够按照本教程前面描述的那样进行类型检查。Ada定义允许编译器编者为了方便起见,要求将泛型规范和主体包含在同一个文件中,因此这取决于用于定义是否可以将泛型过程分成两部分的特定编译器。
当然,通过这样一个简单的过程,很难理解这个新的Ada构造的巨大好处,但是在实际的编程情况下,许多可重用性的情况将变得明显,并且这些好处将得到赏识。
其他形式泛型类型
Example program ------> e_c30_p2.ada
-- Chapter 30 - Program 2 generic type ITEM is private; procedure Exchange_Data(X, Y : in out ITEM); procedure Exchange_Data(X, Y : in out ITEM) is Temp : ITEM; begin Temp := X; X := Y; Y := Temp; end Exchange_Data; generic type ANOTHER_ITEM is range <>; -- Integer class only function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM; function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM is Temporary : ANOTHER_ITEM; begin Temporary := (X + Y) / 2; return Temporary; end Average; -- This is the beginning of the main program which uses the generic -- procedure and function defined above. with Exchange_Data, Average; with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO; procedure SwapMore is type NEW_TYPE is new INTEGER range 122..12345; type MY_RECORD is record My_Grade : INTEGER range 1..128; My_Class : CHARACTER; end record; procedure Swap is new Exchange_Data(INTEGER); procedure Swap is new Exchange_Data(MY_RECORD); procedure Swap is new Exchange_Data(FLOAT); procedure Swap is new Exchange_Data(NEW_TYPE); procedure Swap is new Exchange_Data(CHARACTER); procedure Trade is new Exchange_Data(CHARACTER); procedure Exchange is new Exchange_Data(CHARACTER); procedure Puppy is new Exchange_Data(CHARACTER); function Mean is new Average(INTEGER); function Mean is new Average(NEW_TYPE); function Swap is new Average(INTEGER); -- This is dumb to do, -- but it is legal. Index1 : INTEGER := 117; Index2 : INTEGER := 123; Data1 : MY_RECORD := (15, 'A'); Data2 : MY_RECORD := (91, 'B'); Real1 : FLOAT := 3.14; Real2 : FLOAT := 77.02; Value1 : NEW_TYPE := 222; Value2 : NEW_TYPE := 345; begin Put(Real1, 4, 2, 0); Put(Real2, 4, 2, 0); New_Line; Swap(Real1, Real2); Put(Real1, 4, 2, 0); Put(Real2, 4, 2, 0); New_Line(2); Put(Index1, 6); Put(Index2, 6); New_Line; Swap(Index1, Index2); Swap(Data1, Data2); Put(Index1, 6); Put(Index2, 6); New_Line; -- Now to exercise some of the functions Index1 := Mean(Index2, 16); Value1 := Mean(Value2, Value2 + 132); Index1 := Swap(Index1, Index2 + 12); -- This actually gets the -- mean of the inputs. end SwapMore; -- Result of Execution -- 3.14 77.02 -- 77.02 3.14 -- -- 117 123 -- 123 117
检查名为e_c30_p2的程序。Ada提供了另外两个泛型子程序示例。您将注意到,第一个是一个泛型过程,第二个是一个泛型函数,这表明任何一种形式的子程序都可以写成一个泛型单元。第一个子程序与上一个示例程序中的子程序几乎完全相同,唯一的区别是在第3行中声明了形式泛型类型,我们稍后将讨论这一点。第二个子程序,即第16行到第25行中的函数,如果您将ANOTHER_ITEM类型的每次出现替换为INTEGER类型,那么理解起来就很简单。实际上,它对给定的两个值取平均值并返回结果。
为什么是不同的类型?
在第3行中,我们将类型ITEM声明为私有,在名为Average的函数中,我们将类型ANOTHER_ITEM声明为类型的整型类的类型(我们将很快向您展示,只是有点耐心)。就像很多事情一样,选择的类型是一种妥协的结果。如果将类型限制为整数类,那么在泛型子程序中,可以执行为变量的整数类定义的任何操作,例如算术操作、所有6个比较操作和逻辑操作。另一方面,我们限制了泛型子程序可用于的类型的数量,因为它不能用于任何实际类型、记录或数组。在某种意义上,我们限制了通用过程的使用,允许在过程中有更多的使用自由。
第一个子程序将正式泛型声明为私有类型,这严重限制了可在过程中对数据执行的操作。通用过程仅限于赋值,并比较相等和不相等。在泛型过程中不允许进行其他操作。但是,通过将泛型过程中的类型限制为private,我们可以在调用程序中提供更大的灵活性。这个泛型过程可以用任何类型实例化,这些类型可以被赋值或比较相等或不相等,本质上是任何类型。通过限制泛型过程中允许的操作,我们使得它对调用者更有用。
泛型子程序有多灵活?
让我们先转到主程序,我们看到泛型过程Exchange_Data成功实例化了INTEGER、MY_RECORD和FLOAT类型,以及其他一些类型。这个过程是非常灵活的,因为它是用一个非常严格的(在通用过程本身内有限制的)通用形式类型声明的。此外,能够交换两个记录类型的元素、两个整数或两个实变量也是有意义的。
继续在主程序中,您将在第57和58行中看到,使用更自由(泛型函数本身内的自由)类型声明的函数几乎没有那么多的用法。由于在函数中允许对整数进行算术运算,因此不能在此泛型过程的实例化中使用记录类型,因为将两个记录相加没有意义。
看似不必要的限制
您将注意到,由于我们声明泛型形式类型的方式,实类型不能用于函数的实例化。取两个实数的平均值是有意义的,但是这个函数不能这样做因为Ada的定义。原因是不存在既可以是整数又可以是实数的广义标量类型。添加一个会给该语言增加很多额外的包袱,而不会给Ada的实用功能增加太多。即使不增加额外的负担,这种语言已经足够大了。
做一件低效的事
第52行到第55行每个实例化泛型过程Exchange_Data的一个副本,并且每个实例化都使用相同的类型,即CHARACTER。这将导致四个代码段,每个代码段执行相同的操作,唯一的区别是调用过程的名称。如果实际上存在为同一过程使用不同名称的基础,则应该使用重命名功能,因为它只会为每个新名称创建一个别名。只需要一段代码。
一件非常愚蠢的事
第59行包含泛型函数Average的实例化,该函数使用INTEGER类型并将其命名为Swap,这个名称在第48到52行中已经重载了5次。Ada系统足够聪明,可以通过比较类型并识别这是一个函数调用这一事实来确定您真正想做什么,并且它将完全按照您的要求来做。在任何Ada程序中,为函数使用非描述性名称都是非常糟糕的做法,但如果使用一个已经用于其他目的的名称就更糟糕了。Ada编译器可以正确地处理这个问题,但是您以后可能会很难破译它。
正如前面所讨论的,这个文件可以被分成至少三个不同的文件并分别编译,在某些编译器中,它可以被分成五个文件。
快速回顾一下
请记住,在为泛型形参选择类型时,如果选择子程序中非常严格的类型,则用户可以将子程序与许多类型一起使用。如果选择子程序中相当宽松的类型,则用户不能将生成的泛型子程序与同样多的类型一起使用。在理解了程序操作的细节之后,请确保编译并执行该程序。
什么是形式泛型类型?
Example program ------> e_c30_p3.ada
-- Chapter 30 - Program 3 generic type LIMPRIV is limited private; -- Limited private type type PRIV is private; -- Private type type DISCRETE_TYPE is (<>); -- Discrete type type INT_TYPE is range <>; -- Integer type type MOD_TYPE is mod <>; -- Modular type type FLOAT_TYPE is digits <>; -- Floating point type type FIXED_TYPE is delta <>; -- Fixed point type type DECIMAL_TYPE is delta <> digits <>; -- Decimal type procedure AllGener; procedure AllGener is begin null; end AllGener; generic type LIMPRIV is limited private; type PRIV is private; type DISCRETE_TYPE is (<>); type MOD_TYPE is mod <>; type FLOAT_TYPE is digits <>; type FIXED_TYPE is delta <>; procedure ManyUsed(A, B, C : MOD_TYPE; Data_In : LIMPRIV; More_Dat : PRIV; List_Of : DISCRETE_TYPE; Real_Dat : FLOAT_TYPE; Fix_Dat : FIXED_TYPE); procedure ManyUsed(A, B, C : MOD_TYPE; Data_In : LIMPRIV; More_Dat : PRIV; List_Of : DISCRETE_TYPE; Real_Dat : FLOAT_TYPE; Fix_Dat : FIXED_TYPE) is Index2 : MOD_TYPE := 4; Index3 : INTEGER := 7; type NEW_INTEGER is new INTEGER range 12..34; Small_Int : NEW_INTEGER := 17; begin Index2 := A + B + C + MOD_TYPE(Index3) + MOD_TYPE(Small_Int); end ManyUsed; -- Result of execution -- (No results, this is not executable)
检查名为e_c30_p3的程序。Ada是几乎所有可用的形式泛型类型的例子。我们将陆续详细地研究每一个。从第3行所示的有限的私有类型开始,在子程序中没有自由,但它可以与调用程序中的任何类型一起使用。私有类型在泛型子程序中非常有限,但在调用程序中几乎允许任何类型。
离散形式参数类型
离散形式参数类型是用保留字类型后跟类型名声明的,保留字是,括号内的方框“<>”如图示。这里使用的类型名可以与离散类的任何实际类型匹配。这些类型是整数类类型、枚举类型、BOOLEAN和CHARACTER类型。在泛型子程序中,可以使用可以对枚举变量进行的任何操作。这解释了为什么必须为整数类型变量定义POS和ORD属性,如前面的课程中所述。因为这些属性是可用的,所以整数类型可以与这种正式的泛型类型一起使用。
整型类形式参数类型
整数形式参数类型(如第6行所示)是用保留字类型后跟类型名称声明的,保留字是和范围,以及“框”(如所示)。这里使用的类型名可以与整数类的任何实际类型匹配。在泛型子程序中,除了离散泛型形式参数提供的所有这些运算之外,还可以进行算术运算。但如您所料,不能用枚举类型实例化使用此泛型形式参数的泛型子程序。
真正的形式参数类型
实际形式参数类型的声明如第7行和第8行所示,保留字digit或delta表示浮动或固定的实际类型。泛型子程序中只允许那些类型允许的操作,调用程序在实例化泛型单元的副本时,只能使用相应的实际类型。
第9行给出了一个实际不使用任何类型的过程。这个过程的主体在第11行到第14行中给出,但是它不是很有趣,因为它不做任何事情。第二个过程名为ManyUsed,它以类似的方式列出了所有类型,但声明了一个使用所有6个形式参数的过程作为说明。程序本身很少使用声明的接口。
这个程序的细节将留给你研究,之后你应该编译它。因为它只由泛型子程序组成,所以它是不可执行的。
数组类型形式泛型参数
Example program ------> e_c30_p4.ada
-- Chapter 30 - Program 4 -- Constrained array generic element generic type SUBSCRIPT is (<>); type FLOAT_TYPE is digits <>; type CONSTRAINED_ARRAY is array (SUBSCRIPT) of FLOAT_TYPE; procedure SumArray(Input_Array : CONSTRAINED_ARRAY; Sum_Of_Elements : in out FLOAT_TYPE); procedure SumArray(Input_Array : CONSTRAINED_ARRAY; Sum_Of_Elements : in out FLOAT_TYPE) is begin Sum_Of_Elements := 0.0; for Index in Input_Array'FIRST..Input_Array'LAST loop Sum_Of_Elements := Sum_Of_Elements + Input_Array(Index); end loop; end SumArray; -- Unconstrained array generic element generic type MY_TYPE is range <>; type MY_ARRAY is array (POSITIVE range <>) of MY_TYPE; function AddArray(Input_Array : MY_ARRAY) return MY_TYPE; function AddArray(Input_Array : MY_ARRAY) return MY_TYPE is Sum_Of_Elements : MY_TYPE := 0; begin for Index in Input_Array'FIRST..Input_Array'LAST loop Sum_Of_Elements := Sum_Of_Elements + Input_Array(Index); end loop; return Sum_Of_Elements; end AddArray; with Ada.Text_IO, SumArray, AddArray; use Ada.Text_IO; procedure ArrayGen is type LIMIT_RANGE is new INTEGER range 1..5; type MY_FLOAT is new FLOAT digits 5; type FLOAT_ARRAY is array(LIMIT_RANGE) of MY_FLOAT; procedure Little_Sum is new SumArray(LIMIT_RANGE, MY_FLOAT, FLOAT_ARRAY); Total : MY_FLOAT; My_Data : FLOAT_ARRAY := (1.0, 2.2, 4.3, 3.1, 5.4); type FLEX_ARRAY is array(POSITIVE range <>) of INTEGER; Big_Data : FLEX_ARRAY(1..5) := (13, 12, 17, 33, 41); More_Data : FLEX_ARRAY(12..14) := (34, 101, 56); Result : INTEGER; function My_Add is new AddArray(INTEGER, FLEX_ARRAY); begin Little_Sum(My_Data,Total); Little_Sum((2.4, 1.3, 5.2, 4.7, 0.1),Total); Result := My_Add(Big_Data); Result := My_Add(More_Data); end ArrayGen; -- Result of execution -- (No output from this program)
检查名为e_c30_p4的程序。Ada提供了数组类型泛型形式参数的例子。带有约束数组类型泛型形参的过程的规范和主体在第3行到第17行中给出。这里列出了三个通用的形式参数,在编号为4到6的三行中,每一行都有一个。
要定义如何使用这个过程,我们将引用该文件的第49行中的实例化调用,它正好在主程序的声明部分中。必须用任意离散类型的实际参数替换通用形式名称下标,因为类型标记在圆括号中包含“<>”。此外,实际参数列表中的第一个元素将替换此参数,因为它是正式参数列表中的第一个元素。为了开始在第49行中请求的实例化,我们可以将第4行到第17行中出现的单词下标替换为单词LIMIT_RANGE,这样就可以创建一个可用的过程。
实际参数列表的第二个参数是名为MY_FLOAT的类型,它将用于替换泛型形式参数第二行中列出的名为FLOAT_TYPE的泛型形式类型。您将注意到所需的类型是任意浮点类型,因为第5行中出现了保留字数字。实际参数的类型如第50行所列,并在第46行中定义为浮点类型。
约束数组泛型参数
形式参数的第三行,即第6行,声明了可以使用给定实例化的受约束的数组。您将注意到这个形参依赖于前面声明的两个形参,但是由于这一行中只有一个新形参,Ada编译器可以正确地将实际类型FLOAT_ARRAY赋值为名为CONSTRAINED_ARRAY的形参的替代品。如果您实际复制了这个泛型过程,并将形式参数替换为它们相应的实际参数,然后将过程的名称更改为Sum_Array,您可以将第49行和第50行中的实例化替换为结果过程,程序操作将是相同的。第64行和第65行给出了使用实例化过程的示例,将留给您研究。
无约束数组泛型参数
第22行到第34行给出了一个使用无约束数组作为泛型形式参数的示例,它与上一个示例几乎相同,只是索引类型被声明为POSITIVE类型,而不是像上一个示例那样灵活。索引变量的类型可以声明为泛型类型参数,在第24行之前添加另一行,并且实例化语句将需要三种类型,而不是两种。第60行是实例化语句的示例,第67和68行给出了结果函数的使用示例。
正如前面多次提到的,每个子程序都可以被分离到单独的文件中,并分别编译,然后由任何调用程序使用。请确保编译并执行此程序。
访问形式泛型参数
Example program ------> e_c30_p5.ada
-- Chapter 30 - Program 5 generic type ACCESS_INT is access INTEGER; procedure Swap_Int_Data(Dat1, Dat2 : ACCESS_INT); procedure Swap_Int_Data(Dat1, Dat2 : ACCESS_INT) is Temp : ACCESS_INT := new INTEGER; begin Temp.all := Dat1.all; Dat1.all := Dat2.all; Dat2.all := Temp.all; end Swap_Int_Data; generic type ANY_TYPE is private; type ACCESS_ANY is access ANY_TYPE; procedure Swap_Any_Data(Dat1, Dat2 : ACCESS_ANY); procedure Swap_Any_Data(Dat1, Dat2 : ACCESS_ANY) is Temp : ACCESS_ANY := new ANY_TYPE; begin Temp.all := Dat1.all; Dat1.all := Dat2.all; Dat2.all := Temp.all; end Swap_Any_Data; with Ada.Text_IO, Swap_Int_Data, Swap_Any_Data; use Ada.Text_IO; procedure AccesGen is type ACCESS_INT is access INTEGER; Address1, Address2 : ACCESS_INT := new INTEGER; procedure Transpose_Integers is new Swap_Int_Data(ACCESS_INT); type NAME_ARRAY is array(1..6) of CHARACTER; type PERSONAL_STUFF is record Age : INTEGER; Grade : INTEGER; Name : NAME_ARRAY; end record; type PERSONAL_ACCESS is access PERSONAL_STUFF; Male_Student : PERSONAL_ACCESS := new PERSONAL_STUFF; Female_Student : PERSONAL_ACCESS := new PERSONAL_STUFF; type ACCESS_FLOAT is access FLOAT; Address_Float1 : ACCESS_FLOAT; Address_Float2 : ACCESS_FLOAT; procedure Transpose_Floats is new Swap_Any_Data(FLOAT, ACCESS_FLOAT); procedure Transpose_Records is new Swap_Any_Data(PERSONAL_STUFF, PERSONAL_ACCESS); procedure Transpose_Ints is new Swap_Any_Data(INTEGER, ACCESS_INT); begin Put_Line("Begin the generic access routine"); Address1.all := 23; Address2.all := 13 * Address1.all; Transpose_Integers(Address1, Address2); Transpose_Ints(Address1, Address2); Address_Float1 := new FLOAT; Address_Float2 := new FLOAT; Address_Float1.all := 3.141592; Address_Float2.all := 144.0; Transpose_Floats(Address_Float1, Address_Float2); Male_Student.all := (16, 11, "Johnny"); Female_Student.all := (15, 11, "Sandy "); Transpose_Records(Male_Student, Female_Student); end Accesgen; -- Result of execution -- Begin the generic access routine
检查名为e_c30_p5的程序。我们首先看一下最后一个泛型形式参数类型,访问类型。第一个过程在第2行到第14行中使用了只匹配访问INTEGER的访问类型的访问类型,这导致很少或没有灵活性,因为很少使用对INTEGER类型变量的访问类型。在主程序的第45行实例化了这个泛型过程的一个副本,在第76行执行了这个过程,作为您学习的一个例子。
第19行到第32行给出的第二个过程演示了一个更有用的过程,因为这个过程可以用于任何访问类型,包括数组和记录的复合类型。在第64行到第68行中实例化了泛型过程的三个副本,并且所有这三个副本都在主程序中执行。请注意,在第77行中使用了更通用的过程,将INTEGER类型变量转置回原来的位置。
这个程序的细节应该很容易理解,因此不需要作出额外的注释。请确保编译并执行此程序。
编程练习
1。给e_c30_p1添加一个INTEGER的子类型。ada,并确定如果除了为INTEGER实例化外,还为子类型实例化Exchange_Data的副本会发生什么。在主程序中添加语句来交换新类型的值。(Solution)
2。尝试实例化使用e_c30_p1.ada类型的Average的副本。(Solution)
3。尝试在Exchange_Data体中执行违反私有类型的加法操作。(Solution)
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人