近几年来,面向对象编程的流行程度有了很大的提高。似乎这个术语已经应用于每一种编程语言和营销方案,而不管它是否真的是产品的一部分。可能很少有人真正了解它是什么,知道如何正确地使用面向对象编程,但这并不妨碍我们深入研究并学习如何在程序中使用某些技术。我们将在下两章专门讨论这个问题。
定义和说明诸如面向对象分析和面向对象设计之类的主题超出了本教程的范围。有很多书可以很好地涵盖这些主题,所以我们将花时间展示ada95中的各种结构如何帮助我们构建一个更可靠的程序。
通常应用于面向对象编程的三个术语是封装、继承和多态或动态绑定。我们已经在本教程的第21章中介绍了一个很好的封装,可以称为信息隐藏,我们将把多态性的讨论推迟到第23章。这留给我们的遗产,我们将在这个时候讨论。
继承和扩展
继承包括复制某个现有实体,并将其添加到该实体中,以定义一个具有原始实体的所有属性但具有添加属性的实体。大多数其他编程语言在处理这个主题时都强调前面语句的继承部分,而很少强调扩展操作。然而,Ada的作者将重点放在该语句的扩展部分上,因此我们也将这样做。在本章中,我们将说明如何从一个实体开始,并对其进行扩展,使其具有额外的功能。像往常一样,我们将从一个简单的示例程序开始。
最简单的继承
Example program ------> e_c22_p1.ada
-- Chapter 22 - Program 1 package Starter is type MY_INTEGER is new INTEGER range 5..150; type MY_FLOAT is new FLOAT; type WOODEN_BOX is record Length : FLOAT; Width : FLOAT; Height : FLOAT; end record; function "+" (Left, Right : WOODEN_BOX) return WOODEN_BOX; type STEEL_BOX is new WOODEN_BOX; function "-" (Left, Right : STEEL_BOX) return STEEL_BOX; end Starter; -- There is no implementation for this example program. -- Result of execution -- -- (This fragment cannot be executed)
这个程序与通常应用于编程语言的面向对象编程无关,但它演示了一些Ada类型的简单扩展。第4行声明MY_INTEGER 类型将具有INTEGER 类型的所有属性,但范围更为有限,因此它实际上是从父类型继承其所有属性。关于第6行中的MY_FLOAT 类型,也可以这样说。但是,这种继承不是很有趣,因此我们将继续讨论更复杂的类型。
我们在第8行到第13行中定义了一个名为WOODEN_BOX 的类型,它由三个简单的组件组成,并且具有所有记录都具有的几个预定义操作,例如赋值、相等比较和不等比较。我们添加了另一个基本操作,即通过重载第15行中的+运算符来添加两个此类对象的能力。在第17行中,我们派生了一个名为STEEL_BOX 的新类型,它将包含其父类型所具有的所有组件和操作,包括+运算符的重载。有趣的部分在第19行,我们通过重载- 运算符扩展了STEEL_BOX类型。应该清楚的是,我们“继承”了WOODEN_BOX 类型的所有功能,并通过附加操作符扩展了STEEL_BOX 的操作。
这是继承和扩展,但非常有限,因为我们不能向继承的功能添加组件,只能添加操作。我们将在本章后面的示例程序中添加组件,但给出这个程序片段是为了说明一种非常基本的继承形式。
更简单的类型扩展
Example program ------> e_c22_p2.ada
-- Chapter 22 - Program 2 package Conveyance1 is -- This is a very simple transportation type. type TRANSPORT is record Wheels : INTEGER; Weight : FLOAT; end record; procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT; -- This CAR type extends the functionality of the TRANSPORT type. type CAR is new TRANSPORT; function Tire_Loading(Vehicle_In : CAR) return FLOAT; end Conveyance1; package body Conveyance1 is -- Subprograms for the TRANSPORT record type. procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) is begin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In; end Set_Values; function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is begin return Vehicle_In.Wheels; end Get_Wheels; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is begin return Vehicle_In.Weight; end Get_Weight; -- Subprogram for the CAR record type. function Tire_Loading(Vehicle_In : CAR) return FLOAT is begin return Vehicle_In.Weight / FLOAT(Vehicle_In.Wheels); end Tire_Loading; end Conveyance1; -- Results of execution -- -- (This package cannot be executed alone.)
示例程序e_c22_p2.ada演示了更多的扩展。它首先定义了一个名为TRANSPORT的非常简单的记录,除了由系统自动生成的运算符(如赋值和比较等)外,还包含两个组件和三个显式声明的子程序。这张唱片没有什么神奇或不同寻常的地方。它可以用来定义数据,并根据Ada的规则使用,就像我们在本教程中研究的任何其他记录一样。实际上,我们将在下一个示例程序中使用它。
下一个名为CAR的记录更有趣,因为它被声明为TRANSPORT 记录的派生类型。这意味着它拥有其父级的所有组件和功能。事实上,我们可以使用这个新类型,就好像它有第11行到第15行中声明的三个函数,用类型CAR的第一个参数重新定义一样。所有这三个子程序都可以用CAR类型的变量作为第一个参数直接调用,而无需对变量进行任何类型转换。我们通过添加一个名为Tire_Loading 的函数来扩展CAR,使其比其父类型具有更多的功能。这确实是最完整意义上的继承和扩展,但我们仍然没有能力向新类型添加组件,只有功能。我们很快就能扩展组件。
package body中的实现非常简单,使用您已经获得的Ada知识理解它应该不会有困难。请注意,TRANSPORT类型的三个子程序可用于CAR类型,就像它们在这里被重新定义一样,但它们不是,因为它们是从父类型自动继承的。从旧记录派生新记录时,所有运算符和基本子程序都会自动继承。
什么是原始操作?
一个类型的基本操作是:
1、编译器预定义的所有内部操作,如赋值、比较是否相等和属性
2、使用继承或派生时从父级继承的所有基元操作
3、具有一个或多个参数和/或类型返回值的子程序,这些参数和/或返回值与类型声明本身一起在包规范中声明
本例中的CAR 类型具有每个类别中的一些。它有一些内在的操作符,比如赋值。它继承其父类的三个操作,这些操作在第11到15行中声明。最后,它有一个在第21行中声明的自身独有的操作,即Tire_Loading函数。如果CAR类型被继承到另一个类型中,例如SPORTS_CAR 类型,那么它将继承第11行到第21行中声明的所有四个函数,因为它们都在它的祖先类型中。
使用类型扩展包
Example program ------> e_c22_p3.ada
-- Chapter 22 - Program 3 with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance1; use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance1; procedure Vehicle1 is Hummer : TRANSPORT; Ford : CAR; begin Set_Values(Hummer, 4, 5760.0); Put("The Hummer has"); Put(Get_Wheels(Hummer), 2); Put(" wheels."); New_Line; Put("The Hummer has"); Put(Hummer.Wheels, 2); Put(" wheels."); New_Line; Set_Values(Ford, 4, 3204.0); Put("The Ford has"); Put(Get_Wheels(Ford), 2); Put(" wheels. The tire load is "); Put(Tire_Loading(Ford), 3, 1, 0); Put(" pounds per tire."); New_Line; end Vehicle1; -- Result of execution -- -- The Hummer has 4 wheels. -- The Hummer has 4 wheels. -- The Ford has 4 wheels. The tire load is 801.0 pounds per tire.
检查名为e\_c22_p3.ada的文件,以获取使用上一个示例程序中定义的两种类型的示例。您会注意到,名为conferance1的包在第3行和第4行中带有with'ed和use'ed,以便在程序中使用。我们在第8行和第9行定义了两个变量,我们将在程序中使用这两种类型中的每一种。
第13行到第18行相当简单,没有什么新内容,所以你可以自己学习。第20行到第23行也是如此,只是我们将很快返回这组语句。
第一个非常有趣的语句在第25行给出,我们调用Set_Values ,第一个参数的类型是CAR,尽管我们没有显式定义该类型的函数。这证明系统真正生成了一个以类型CAR为第一参数的函数,该函数与以类型TRANSPORT为第一参数的函数相同。因为我们没有写两次代码,所以我们只有一个函数来维护它,它对这两种类型都起作用,但是如果我们想让它们做不同的事情,我们可以为CAR自己的Set_Values 函数编写一个规范和一个body。
因此,在这个示例程序的第25行和第28行中说明了类型扩展,其中调用了从父类型继承的函数Get_Wheels 。然而,第30行说明了类型扩展,因为我们编写了一个全新的函数来从汽车类型变量检索轮胎装载。您会注意到,由于Ada的明确定义,在这个程序中无法区分哪些函数是继承的,哪些是扩展的。运输类型没有轮胎装载功能。
面向对象编程包含了信息隐藏的概念,我们不使用这个示例程序来实践这个概念。如果你回到第21行,你会看到我们正在访问的车轮数悍马直接。这是一种糟糕的做法,因为计算机编程中最容易出错的操作之一是提供对大范围数据的直接访问。信息隐藏的原理要求将对象的数据隐藏在对象中,并且对外部世界不可用,如本教程上一章所述。在下一个示例程序中,我们将完全隐藏对象中的数据。
在继续下一个示例程序之前,请务必编译并执行此程序,以确保您了解它的功能。
带有专用节的类型扩展
Example program ------> e_c22_p4.ada
-- Chapter 22 - Program 4 package Conveyance2 is type TRANSPORT is private; procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT; type CAR is private; procedure Set_Values(Vehicle_In : in out CAR; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : CAR) return INTEGER; function Tire_Loading(Vehicle_In : CAR) return FLOAT; private -- private part of the specification type TRANSPORT is record Wheels : INTEGER; Weight : FLOAT; end record; type CAR is new TRANSPORT; end Conveyance2; package body Conveyance2 is -- Subprograms for the TRANSPORT record type. procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) is begin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In; end Set_Values; function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is begin return Vehicle_In.Wheels; end Get_Wheels; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is begin return Vehicle_In.Weight; end Get_Weight; -- Subprogram for the CAR record type. procedure Set_Values(Vehicle_In : in out CAR; Wheels_In : INTEGER; Weight_In : FLOAT) is begin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In; end Set_Values; function Get_Wheels(Vehicle_In : CAR) return INTEGER is begin return Vehicle_In.Wheels; end Get_Wheels; function Tire_Loading(Vehicle_In : CAR) return FLOAT is begin return Vehicle_In.Weight / FLOAT(Vehicle_In.Wheels); end Tire_Loading; end Conveyance2;
名为e_c22_p4.ada的示例程序与前面的程序非常相似,只是这两个记录类型被声明为私有。这样做是为了防止用户看到对象并直接操作包含的数据。然而,随着我们对代码的每一项改进,都有可能出现其他东西不能很方便地工作的情况,这里需要付出一些代价。
因为TRANSPORT 类型是私有的,所以它不能为自身之外的对象提供完整的功能。事实上,它允许的唯一操作是赋值和比较是否相等或不相等。在第6行到第10行中为TRANSPORT类型定义的三个子程序不能被继承到CAR类型中,因为此时没有指示CAR从TRANSPORT继承。有必要像第14行到第18行那样,为汽车类型显式地声明它们。如第58行至第74行所示,还需要为这三个功能提供完整的实现。很明显,这种传承形式缺乏优雅。
与所有私有类型一样,在第21行到第29行的私有部分中给出了实际的定义,很明显CAR继承了所有的TRANSPORT。这个示例与上一个示例非常相似,因此您需要自己研究包体。
使用类型扩展名
Example program ------> e_c22_p5.ada
-- Chapter 22 - Program 5 with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance2; use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance2; procedure Vehicle2 is Hummer : TRANSPORT; Ford : CAR; begin Set_Values(Hummer, 4, 5760.0); Put("The Hummer has"); Put(Get_Wheels(Hummer), 2); Put(" wheels."); New_Line; Set_Values(Ford, 4, 3204.0); Put("The Ford has"); Put(Get_Wheels(Ford), 2); Put(" wheels. The tire load is "); Put(Tire_Loading(Ford), 3, 1, 0); Put(" pounds per tire."); New_Line; end Vehicle2; -- Result of execution -- -- The Hummer has 4 wheels. -- The Ford has 4 wheels. The tire load is 801.0 pounds per tire.
名为Vehicle2的示例程序位于名为e_c22_p5.ada的文件中,它给出了使用TRANSPORT和CAR类的非常有限的示例。这里没有什么新的东西,除了一个事实,即车轮组件的直接访问没有在这里使用,因为它现在从视图中隐藏。
下一个示例程序将最终提供组件和操作的完整继承和扩展。这就是在有关面向对象编程的文章中提到继承时通常的含义。
实型扩展
Example program ------> e_c22_p6.ada
-- Chapter 22 - Program 6 package Conveyance3 is -- This is a very simple transportaion type. type TRANSPORT is tagged private; procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT); function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT; type CAR is new TRANSPORT with private; procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER); function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER; type TRUCK is new TRANSPORT with private; procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT); function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER; type BICYCLE is new TRANSPORT with private; private type TRANSPORT is tagged record Wheels : INTEGER; Weight : FLOAT; end record; type CAR is new TRANSPORT with record Passenger_Count : INTEGER; end record; type TRUCK is new TRANSPORT with record Passenger_Count : INTEGER; Payload : FLOAT; end record; type BICYCLE is new TRANSPORT with null record; end Conveyance3; package body Conveyance3 is -- Subprograms for the TRANSPORT3 record procedure Set_Values(Vehicle_In : in out TRANSPORT; Wheels_In : INTEGER; Weight_In : FLOAT) is begin Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In; end Set_Values; function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is begin return Vehicle_In.Wheels; end Get_Wheels; function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is begin return Vehicle_In.Weight; end Get_Weight; -- Subprograms for the CAR record procedure Set_Values(Vehicle_In : in out CAR; Passenger_Count_In : INTEGER) is begin Vehicle_In.Passenger_Count := Passenger_Count_In; end Set_Values; function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER is begin return Vehicle_In.Passenger_Count; end Get_Passenger_Count; -- Subprograms for the TRUCK record procedure Set_Values(Vehicle_In : in out TRUCK; Wheels_In : INTEGER; Weight_In : FLOAT; Passenger_Count_In : INTEGER; Payload_In : FLOAT) is begin -- This is one way to set the values in the base class Vehicle_In.Wheels := Wheels_In; Vehicle_In.Weight := Weight_In; -- This is another way to set the values in the base class Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In); -- This sets the values in this class Vehicle_In.Passenger_Count := Passenger_Count_In; Vehicle_In.Payload := Payload_In; end Set_Values; function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER is begin return Vehicle_In.Passenger_Count; end Get_Passenger_Count; end Conveyance3; -- Result of execution -- -- (This program cannot be executed alone.)
名为e_c22_p6.ada的示例程序包含了我们的第一个实际继承和扩展示例,它是实际使用方法的一个很好的示例。最后几个示例程序,即使它们是有效的Ada程序,也没有说明真正的继承,而是意在说明Ada语言中构建了更微妙的继承形式。
我们首先在第5行声明一个private TRANSPORT,但是添加了一个额外的保留字,即tagged。标记的单词向Ada编译器表明,可以使用附加的组件和/或操作来扩展该类型,以定义一个全新的类型。实际的定义在第35行到第39行中给出,现在看起来应该非常熟悉了,除了这里添加的保留字tagged。第7行到第11行中声明了三个子程序,其方式与本章前面的程序中添加的方式大致相同。除了添加单词tagged之外,这个包与前面的示例包相同。
现在看看真正的区别
当我们定义CAR 类型时,情况看起来与上一个程序中的情况大不相同。这次声明中提到了父类型以及保留字with的新用法。我们一直使用这个词with来将包添加到我们的各种程序中,但是现在它用来表示将要向父类型添加一些新的内容来生成新类型。第41行到第44行包含了新型CAR的完整声明。它说要创建一个新的派生TRANSPORT 类型,因为这里使用了保留字with。名为Passenger_Count 的组件与来自TRANSPORT的两个组件一起包含在CAR type中,为CAR type提供了三个变量。我们终于找到了一种方法来扩展继承类型中组件的数量。请注意,只能添加组件。无法删除零部件或替代零部件。
CAR type还从TRANSPORT继承了这三个子程序,但它提供了自己的Set_Values 过程,该过程只设置Passenger_Count 组件的值。大概,CAR类型的对象需要使用父类提供的Set_Values 过程来设置两个继承组件的值。这就是下一个示例程序中实际要做的。CAR type还提供了一个名为Get_Passenger_Count 的新子程序来返回乘客数。总的来说,CAR类型由三个组件组成,并提供了五个子程序,可用于操作此类对象。
可以通过添加子程序来扩展类型,也可以通过重写一个或多个子程序来修改类型的功能,但是不可能从新类型中删除作为父类型一部分的子程序。即使TRANSPORT 类型是私有的,CAR 也会继承TRANSPORT 类型的所有基本操作,因为TRANSPORT 类型被标记了。这是真正的继承,因为所有的实体都是继承的。
冻结类型意味着什么?
我们可以将我们希望的所有功能添加到类型中,直到我们使用该类型定义变量或将其继承到新类型。使用后,我们不允许向类型添加功能,因为我们将有两种类型的变体,一种是使用它的,另一种是更新的更改版本。尽管两个类型名称相同,但两者都不兼容。因此,类型为“冻结”,这意味着您无法向其添加更多的基本操作。
另一种新类型
由于TRUCK 类型与CAR 类型非常相似,所以除了向继承的部件添加两个组件外,不需要说什么,它还为继承自父级的子程序添加了两个子程序。TRUCK 类型有点不同,因为它在名为Set_Values,的过程中为所有四个变量提供初始化数据,因此它不需要父类型中同名的过程。
BICYCLE 类型在第31行中的声明方式与CAR 和TRUCK完全相同。当我们到达第52行,其中实际定义了私有类型,它看起来与其他两种新类型有点不同。行末尾的with null record告诉系统自行车类型将包含父类型的所有组件,而不再包含。有必要明确地告诉系统,没有一个将被添加。
现在我们可以将组件和子程序添加到继承类型中。我们可以添加子程序而不添加任何组件。我们也可以添加组件而不添加任何附加子程序,但这将不会非常有用,因为我们将无法使用新组件,因为它们将隐藏在对象中。
什么是CLASS?
ada95中的类是一组具有共同祖先的类型,该类的名称是最高祖先的类型名称。本示例程序包含名为Transportation的类,它由TRANSPORT, CAR, TRUCK和BICYCLE组成。如果说它包含的是一个名为CAR的类,它只包含类型的CAR,这也是正确的。任何类型的层次结构都组成一个类,其类名是层次结构中最高类的名称。目前,类概念并不重要,但在下一章中,研究动态调度将变得重要。
包装体
第58行至121行中给出的包主体与本章中使用的相同实现无异。所有继承和扩展构造都在代码的规范部分中。
学生应该注意第104和105行中的一个小细节,其中从父类继承的组件被初始化。这里没有什么新的,但是在第108行中,父类中名为Set_value的过程用于初始化相同的两个变量。由于类型不同,因此需要执行从TRUCK 类型到TRANSPORT 类型的类型转换,以使编译器接受它。注意,可以使用任何初始化方法。这里给出了这两种方法的示例。
使用实型扩展
Example program ------> e_c22_p7.ada
-- Chapter 22 - Program 7 with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3; use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3; procedure Vehicle3 is Hummer : TRANSPORT; Limo : CAR; Chevy : CAR; Dodge : TRUCK; Ford : TRUCK; begin Set_Values(Hummer, 4, 5760.0); Set_Values(Limo, 8); Set_Values(TRANSPORT(Limo), 4, 3750.0); Set_Values(Chevy, 5); Set_Values(TRANSPORT(Chevy), 4, 2560.0); Set_Values(Dodge, 6, 4200.0, 3, 1800.0); Set_Values(Ford, 4, 2800.0, 3, 1100.0); Put("The Ford truck has"); Put(Get_Wheels(Ford), 2); Put(" wheels, and can carry"); Put(Get_Passenger_Count(Ford), 2); Put(" passengers."); New_Line; end Vehicle3; -- Result of execution -- -- The Ford truck has 4 wheels, and can carry 3 passengers.
检查名为e_c22_p7.ada的文件,该文件演示了如何使用我们刚刚生成的这些新类型。正如预期的那样,新的软件包在这个程序中同时带有with'ed和use'ed,并且这四种新类型可以根据需要使用。新类型没有什么特别之处,只是有三个是第四个的后代。通过检查这个示例程序可以看出,它们的使用方式与任何其他类型一样。您将注意到,设置CAR类型对象中的所有值需要两个过程调用,但是设置TRUCK类型对象中的所有值只需要一个调用。这只是因为我们在包中声明它们的方式。在实际程序中,最好以类似的方式声明它们,以使代码更易于理解。
一定要编译并执行这个程序。
初始化器和终结器-受控
Example program ------> e_c22_p8.ada
-- Chapter 22 - Program 8 with Ada.Text_IO, Ada.Finalization; use Ada.Text_IO, Ada.Finalization; package Component is type WIDGET is new CONTROLLED with record Size : INTEGER; Number : INTEGER; end record; procedure Initialize(Item : in out WIDGET); procedure Adjust(Item : in out WIDGET); procedure Finalize(Item : in out WIDGET); end Component; package body Component is procedure Initialize(Item : in out WIDGET) is begin Put_Line(" This is from Initialize"); end Initialize; procedure Adjust(Item : in out WIDGET) is begin Put_Line(" This is from Adjust"); end Adjust; procedure Finalize(Item : in out WIDGET) is begin Put_Line(" This is from Finalize"); end Finalize; end Component; with Ada.Text_IO, Component; use Ada.Text_IO, Component; procedure Control is Paper : WIDGET; Pencil : WIDGET; begin Put_Line("Beginning this simple program"); Paper.Size := 11; Paper.Number := 25; Put_Line("Paper record filled with data"); Pencil := Paper; Put_Line("Paper copied to Pencil"); Paper := Pencil; Put_Line("Pencil copied to Paper"); end Control; -- Result of execution -- -- This is from Initialize -- This is from Initialize -- Beginning this simple program -- Paper record filled with data -- This is from Finalize -- This is from Adjust -- Paper copied to Pencil -- This is from Finalize -- This is from Adjust -- Pencil copied to Paper -- This is from Finalize -- This is from Finalize
名为e_c22_p8.ada的示例程序演示了对象的自动初始化和对象即将销毁时的自动清理。我们包括Ada.定稿并声明从名为CONTROLLED的类型继承的名为WIDGET的类型。我们用预定义的名称Initialize、Adjust和Finalize重写了CONTROLLED中的三个过程,每个过程都有一个我们刚刚声明的记录类型的参数。包体包含三个过程的定义,这三个过程都很简单,因为它们每个只向监视器输出一行文本。
在主程序中,我们将组件包包含在with列表中,并编写一个只输出几行文本的普通程序。当我们执行这个程序时,我们得到了一点惊喜,因为组件包中的三个过程都是执行的,尽管我们从未调用过它们,但系统确实调用了它们。他们很特别,因为他们每个人都有特殊的工作要做。
Initialize—定义此类型的任何对象时,此过程将自动运行,而无需程序员显式调用它。这是初始化类的组件或包含任何其他需要为此类型的每个对象执行的初始化代码的地方。
调整-分配后,您可能需要对新副本中的数据进行一些清理。为新对象分配新数据后,将自动调用此过程。
Finalize—当您为某个变量赋值时,该变量中包含的数据将被覆盖并永远丢失。您可能希望在覆盖数据之前对其执行一些特殊操作,这是本过程的工作之一。当您处理完一个对象并希望销毁它,或者它超出范围时,如果它有一个访问堆中您处理完的某个对象的访问变量,或者在销毁该对象之前必须完成的其他工作,则需要进行一些清理。此过程由系统自动调用。
您会注意到初始化过程在程序开始之前执行了两次,因为有两个变量。Finalize和Adjust过程都是为两个赋值操作中的每一个执行的,从清单中可以明显看出。当两个对象在程序结束时超出范围时,Finalize过程将为它们中的每一个调用一次。
当您使用相当复杂的类型时,这三个过程可以提供很大的帮助。
编程练习
1.在e_c22_p7.ada中添加一个BICYCLE对象,并对其进行练习,以证明您可以正确使用继承的对象(Solution)
2.向e_c22_p6.ada中的BICYCLE类添加一个新函数,转换盎司为单位的重量。将新函数命名为Number_Of_Ounces。一磅等于16盎司(Solution)
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人