Ada程序设计-第4章 记录(Record)

70 阅读7分钟

4.1 概述(Overview)

    记录则是由命名分量(named component)组成的复合类型,即具有不同属性的数据对象的集合,和C下的结构(structure)、Pascal下的记录(record)类似。Ada 的记录比它们提供的功能更强,也就是限制更少。同时记录扩展(record extension)是 Ada95 中类型扩展(继承)机制的基础,使记录的地位更加突出,关于记录扩展详见 第6章 面向对象特性,为了避免重复,本章对此不作介绍。

4.2 简单记录(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 这些成员。

4.3 访问和设置记录(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

4.4 变体记录 (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 或其他成员前面---变体记录的变量大小是不定的

4.5 无约束记录(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        ");

也是可行的。

    上面一些记录的例子并不好,成员的数据类型太简单(像生日的数据类型,一般是年月日做成员的一个记录),字符串类型太多,手工赋值的话还要数一下有几个字符,实际中也很少这样的用法,一般还是用函数来赋值。这点请注意一下。

4.6 判别式的其它用途

判别式的另一个用途是动态决定其成员长度,如:

type Buffer (Size:Integer) is  
   record  
       High_Buffer(1..Size);  
       Low_Buffer(1..Size);  
   end record;

这样 Buffer 两个成员的大小就取决于 Size 值,在文本处理中这种用法还是挺好的。