Ada记录的属性
记录则是由命名分量(named component)组成的复合类型,即具有不同属性的数据对象的集合,和C 下的结构(structure)、Pascal 下的记录(record) 类似。Ada 的记录比它们提供的功能更强,也就是限制更少。同时记录扩展(record extension)是 Ada95 中类型扩展(继承)机制的基础,使记录的地位更加突出,关于记录扩展详见 第6章 面向对象特性,为了避免重复,本章对此不作介绍。
简单记录(Simple Record)
记录类型的一般声明如下:
type record_name is
record
field name 1: type 1;
field name 2: type 2;
...
field name n: type N;
end record;
record_name 是记录类型的名称,一大堆 filed name 是记录的成员名称,紧跟其后的是该成员的数据类型。
如下面的例子:
type Id_Card is
record
Full_Name : String (1..15);
ID_Number : Positive;
Age : Positive;
Birthday : String (1..15);
Familiy_Address : String (1..15);
Family_telephone : Positive;
Job : String(1..10);
end record;
My_Card :Id_Card;
一个简单ID卡的记录,包含Full_Name,ID_Number,Age,Birthday,Familiy_Address,Family_telephone,Job 这些成员。
访问和设置记录(Access and Set Records)
使用记录的成员时,只需在记录和其成员之间用 “.” 隔开即可。如赋予My_Card中的变量 Full_Name 值 Jack Werlch:
My_Card.Full_Name := "Jack Welch ";
设置记录成员的值和设置数组给人感觉上有点类似,如:
My_Card := ("Jack Welch ", 19830519,45, "Jan 1st 1976 ",
"China ",8127271,"CEO ");
将 ( )中的值依次赋给My_Card 的成员。
相同的数据类型的成员一多,无疑会使人不大明了,因此也可以:
My_Card := ( Full_Name => "Jack Welch ",
ID_Number => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Family_telephone => 8127271;
Job => "CEO ") ;
上面两种表示法可以混用,但按位值在有名的值前面:
My_Card := ( "Jack Welch ",
19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Family_telephone => 8127271;
Job => "CEO ");
但如果为:
My_Card := ( Full_Name => "Jack Welch ",
ID_Number => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
8127271;
"CEO ");
则是非法的。
如果几个相同类型的成员,赋予同一数值,也可以:
My_Card := ( Full_Name => "Jack Welch ",
ID_Number | Family_telephone => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Job => "CEO ");
上例我们假设 ID_Number 和 Family_telephone 值是一样的,为19830519,不同成员间用 | 隔开。
记录类型有时在声明也需要默认值:
type Id_Card is
record
Full_Name : String (1..100) := "Jack Welch ",
ID_Number : Positive := 19830519,
Age : Positive := 45,
Birthday: String (1..20) := "Jan 1st 1976 ",
Familiy_Address :String (1..100):= "China ",
Family_telephone :Positive := 8127271;
Job : String(1..10) := "CEO ");
end record;
My_Card :Id_Card;
将 Jack Welch 的资料当作了 Id_Card 类型的默认值,My_Card 无须赋值,在声明时已经有了前几个例子中所赋的值。
声明常量记录如下:
My_Card :constant Id_Card := ( Full_Name => "Jack Welch ",
ID_Number => 19830519,
Age => 45,
Birthday => "Jan 1st 1976 ",
Familiy_Address => "China ",
Family_telephone => 8127271;
Job => "CEO ";)
和创建其它类型的常量类似,只需在该记录类型前添个 constant。
变体记录 (Variant Record)
在讲变体记录前,先介绍一下记录判别式(record discriminant)的概念。判别式(discriminant)以前没接触过,这里先简单提一下它的定义:一个复合类型(除了数组)可以拥有判别式,它用来确定该类型的参数(具体参见 RM95 3.7 Discriminant)。也就是说,一个复合类型创建时可以有一些参数,在接下去声明该类型的变量时,可以通过那些参数的值来改变变量初始化时所占用内存大小、成员数量、取值等等。这一节以及下一节的无约束记录(unconstrained record)的内容都在记录判别式的范围内,至于其它复合类型将在以后讲述。
变体记录,即它的一些成员存在与否取决于该记录的参数。如我们将 Id_Card 这个记录类型扩充一下:
type Id_Card (Age : Positive := 1) is
record
Full_Name : String(1..15);
ID_Number : Positive;
Birthday : String(1..15);
Familiy_Address : String(1..15);
Family_telephone : Positive;
Job : String(1..10);
case Age is
when 1 .. 18 => School_Address : String(1..15);
when 19 .. 60 => Monthly_Income : Integer;
Working_Address: String(1..15);
when others => null; -- 如果 Age 的值不属于 1..60,成员不改变
end case;
end record;
My_Card : Id_Card ;
Your_Card: Id_Card (Age => 20);
上例中,case Age ... end case 是变体部份,当 Age 值在 1..18 时,动态创建成员 School_Address;当 Age 值在 19..60 时,动态创建成员 Monthly_Income,Working_Address;当 Age 不在 1..60 时,数据成员不改动。在声明判别式时一般应赋予一个默认值,如上例 Age 的默认值为 1 ,这样声明变量 My_Card 时不带参数也可以,默认参数为 1。但如果 Age 没默认值,上例中的 My_Card 声明是非法的。
因此,记录 My_Card 有 Full_Name,ID_Number,Birthday,Familiy_Address,Family_telephone, Job,School_Address这些成员,因为 Age 默认为 1; 记录 Your_Card 中 Age 值为 20,因此有 Full_Name,ID_Number,Birthday,Familiy_Address,Family_telephone, Job, Monthly_Income,Working_Address 这些成员。
最后注意一下,变体部份要在记录类型声明的底部,不能在 Job 或其他成员前面---变体记录的变量大小是不定的
无约束记录(Unconstrained Record)
上面的记录都是受限定的,如 创建 My_Card 后,它的判别式无法再被改动,Monthly_Income,Working_Address这些成员也无法拥有。但如果 ID_Card 的判别式有了初使值,则还有办法使记录动态改变。
如:
type Id_Card (Age : Positive := 1) is
record
Full_Name : String(1..15);
ID_Number : Positive;
Birthday : String(1..15);
Familiy_Address : String(1..15);
Family_telephone : Positive;
Job : String(1..10);
case Age is
when 1 .. 18 => School_Address : String(1..15);
when 19 .. 60 => Monthly_Income : Integer;
Working_Address: String(1..15);
when others => null;
end case;
end record;
My_Card : Id_Card ;-- 以上和上一节的例子一样
....
begin
...
My_Card := (17, "Jack Welch ", 19830519, "Jan 1st 1976 ", "China ",8127271,
"CEO ","Shanghai ");
end;
赋值的时候就有了点特殊。My_Card 在程序内部赋值,但与常规赋值不同,它的第一个值是判别式的值,后面才是成员的值---成员数量按照判别式的值动态改变,上例就多了一个 School_Address 成员。这种情况下,编译器会分配给 My_Card 可能使用的最大内存空间。因此将下句接在上句后面:
My_Card := (17, "Jack Welch ", 19830519, "Jan 1st 1976 ", "China ",8127271,
"CEO ", 78112 ,"Shanghai ");
也是可行的。
上面一些记录的例子并不好,成员的数据类型太简单(像生日的数据类型,一般是年月日做成员的一个记录),字符串类型太多,手工赋值的话还要数一下有几个字符,实际中也很少这样的用法,一般还是用函数来赋值。这点请注意一下。
判别式的其它用途
判别式的另一个用途是动态决定其成员长度,如:
type Buffer (Size:Integer) is
record
High_Buffer(1..Size);
Low_Buffer(1..Size);
end record;
这样 Buffer 两个成员的大小就取决于 Size 值,在文本处理中这种用法还是挺好的。