ADA 95教程 高级特性 异常EXCEPTIONS

异常对你来说并不新鲜

假设您已经完成了本教程的第1部分,那么您已经看到了许多对异常的引用,但是我们很少谈到如何使用它们。本章的目的是指导您如何使用异常,当您完成时,您将能够使用异常来开发具有自身错误处理能力的程序。

 

为什么我们需要异常?

Ada开发的最初章程包括在实时环境中操作的能力。如果你有丰富的编程经验,你已经明白,如果一个错误有可能浮出水面,它最终会浮出水面。如果检测到“致命错误”,许多编程语言只是终止操作,但如果程序控制的是人类生命或安全所依赖的实时系统,则这可能是一场灾难。一架747飞机在最后进近机场,或者一个在医院手术室使用的系统就是两个系统的例子,因为一个坏的数据点以某种方式积累起来,所以不能突然终止。小心地应用Ada异常将允许软件从这种情况中优雅地恢复,而不是完全中止操作。

 

我们的第一个异常

Example program ------> e_c17_p1.ada

-- Chapter 17 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Except1 is

   procedure Divide_Loop is
   Divide_Result : INTEGER;
   begin
      for Index in 1..8 loop
         Put("Index is");
         Put(Index, 3);
         Put(" and the answer is");
         Divide_Result := 25 / (4 - Index);
         Put(Divide_Result, 3);
         New_Line;
      end loop;
   exception
      when Constraint_Error => Put_Line(" Divide by zero error.");
   end Divide_Loop;

begin
   Put_Line("Begin program here.");
   Divide_Loop;
   Put_Line("End of program.");
end Except1;




-- Result of Execution

-- Begin program here.
-- Index is  1 and the answer is  8
-- Index is  2 and the answer is 12
-- Index is  3 and the answer is 25
-- Index is  4 and the answer is Divide by zero error.
-- End of program.

 

检查名为e_c17_p1.ada的程序,这是我们的第一个带有异常处理程序的示例程序。暂时忽略第18行和第19行,您将拥有一个完全正常的程序,并且应该不会对您的理解造成任何问题。然而,这个程序确实有一个小心引入的错误,因为当第14行的index达到4时,我们将试图除以0。在任何编程语言中都不允许被零除,因为答案是无限的,因此没有定义。根据定义,Ada运行时系统将导致引发名为Constraint_Error的异常,这是Ada表示试图除以零的方式。这就向系统发出信号,让它做些什么。(实际上,有许多其他方法可以引发Constraint_Error异常,但我们稍后会担心它们。)

Ada系统将以非常明确的方式搜索我们给出的关于这个错误的任何指令,如果没有找到任何指令,将在发出关于这个错误的消息后终止程序的操作。如果我们已经给出了如何处理错误的指令,它将执行这些指令,并按照我们的指示继续操作。第18行和第19行说明了向系统提供这些指令的方法。

 

如何处理异常?

当引发任何异常时,系统会立即在当前块或子程序的末尾查找保留字exception。如果找到了异常,并且在那里定义了引发的特定异常,则执行与该异常相关联的指令,并退出子程序或块。

要定义特定异常的处理程序,请在使用保留字when ,后跟异常的名称,最后是要在=>运算符后执行的语句序列。语句序列可以是任意复杂的,但是由于异常处理的性质,应该保持简单。在这种情况下,我们向监视器输出一条消息,而不执行其他操作。在任何块或子程序的末尾,通过添加表单的其他构造,可以处理所需的任意多个不同的异常,

     when <exception-name> => instructions;

在保留字exception的单个实例之后。我们将在本章后面研究多个异常处理程序的示例。

 

异常处理之后会发生什么?

在异常处理之后,程序执行对调用程序的正常返回,并且执行调用程序中的正常指令序列。请注意,不可能跳回子程序或块,在该子程序或块中,异常是从该块末尾的异常处理例程引发的。在这种情况下,由于使用了逻辑,第10行中定义的循环提前终止,因为我们实际上跳出了循环,程序结束了。如果包含一个异常处理程序或一组异常处理程序,则它必须是块中的最后一个。如果块的正常执行通过进入保留字exception到达可执行语句的结尾,则块正常终止。不能在块的末尾放入异常处理程序。获取异常处理代码的唯一方法是引发异常。

尽管在这一点上还有其他问题,编译并执行这个程序。观察结果,如果您不理解输出,请重新阅读上面的文本,直到您理解为止,因为这些基本点对于理解整个异常主题至关重要。

 

让我们使用几个例外

Example program ------> e_c17_p2.ada

                                       -- Chapter 17 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Except2 is

   procedure Divide_Loop(Index : in     INTEGER) is
   Divide_Result : INTEGER;
   begin
      Put("Index is");
      Put(Index, 3);
      Put(" and the answer is");
      Divide_Result := 25 / (4 - Index);
      Put(Divide_Result, 4);
      New_Line;
   exception
      when Constraint_Error => Put(" Divide by zero error");
                               Put_Line(" in loop 1.");
   end Divide_Loop;

   procedure New_Divide_Loop(Index : in     INTEGER) is
      My_Own_Exception : exception;
      Divide_Result : INTEGER;
   begin
      Put("Index is");
      Put(Index, 3);
      Put(" and the answer is");
      if Index = 4 then
         raise My_Own_Exception;
      end if;
      Divide_Result := 25 / (4 - Index);
      Put(Divide_Result, 4);
      New_Line;
   exception
      when My_Own_Exception => Put(" Divide by zero error");
                               Put_Line(" in loop 3.");
      when Constraint_Error => Put_Line("This shouldn't happen.");
                               Put_Line("But is included anyway.");
      when others => Put_Line("Some other exception found.");
   end New_Divide_Loop;

begin
   Put_Line("Begin program here.");
   for Count in 1..6 loop              -- begin loop number 1
      Divide_Loop(Count);
   end loop;
   Put_Line("End of first loop.");

   for Count in 1..6 loop              -- begin loop number 2
      declare
         Divide_Result : INTEGER;
      begin
         Put("Count is");
         Put(Count, 3);
         Put(" and the answer is");
         Divide_Result := 25 / (4 - Count);
         Put(Divide_Result, 4);
         New_Line;
      exception
         when Constraint_Error => Put(" Divide by zero error");
                                  Put_Line(" in loop 2.");
      end;
   end loop;
   Put_Line("End of second loop.");

   for Count in 1..6 loop              -- begin loop number 3
      New_Divide_Loop(Count);
   end loop;
   Put_Line("End of program.");

end Except2;




-- Result of Execution

-- Begin program here.
-- Index is  1 and the answer is   8
-- Index is  2 and the answer is  12
-- Index is  3 and the answer is  25
-- Index is  4 and the answer is Divide by zero error in loop 1.
-- Index is  5 and the answer is -25
-- Index is  6 and the answer is -12
-- End of first loop.
-- Count is  1 and the answer is   8
-- Count is  2 and the answer is  12
-- Count is  3 and the answer is  25
-- Count is  4 and the answer is Divide by zero error in loop 2.
-- Count is  5 and the answer is -25
-- Count is  6 and the answer is -12
-- End of second loop.
-- Index is  1 and the answer is   8
-- Index is  2 and the answer is  12
-- Index is  3 and the answer is  25
-- Index is  4 and the answer is Divide by zero error in loop 3.
-- Index is  5 and the answer is -25
-- Index is  6 and the answer is -12
-- End of program.

 

检查名为e_c17_p2.ada的程序以获取其他异常示例。这个程序将回答您关于异常的许多问题。

在上一个程序中,我们在引发异常时终止了循环,但我们可能希望在异常之后继续执行程序。此程序中的第一个过程在逻辑上与最后一个示例程序相同,只是循环被移动到调用程序。当系统检测到被零除时,引发了约束错误异常,异常由第17行和第18行中定义的异常处理程序处理,并影响返回到调用程序。然而,在这种情况下,当控制返回到调用程序时,我们仍然在循环中,循环正常完成。这应该告诉您,通过仔细选择处理异常的位置,您可以控制整个结果。当我们继续研究异常时,我们将看到更多关于这一点的内容。

第49行到64行中第二组指令的逻辑与最后一段中研究的第一组指令的逻辑相同。唯一的区别是,过程已更改为块,并以内联方式插入到代码中。这样做是为了说明异常在代码块中的使用,并说明代码块的异常处理程序放在该块的末尾。在引发并处理异常之后,从块后面的第一条语句开始执行。因为块包含在循环中,所以异常在循环中处理,循环运行到完成。

 

多个异常处理程序

最后,我们来到第66行到第69行的代码部分,它由一个简单的循环组成,该循环调用过程New_Divide_loop。在第21行到第40行中定义的过程本身包含了一个新操作的示例,它能够构造我们自己的异常,自己引发异常,并使用我们自己的异常处理程序处理异常。

第22行将标识符MyOwnException声明为异常的名称,其定义方式与声明变量的方式大致相同。我们不能给它赋值,但是我们可以在它定义的作用域内的任何地方提升它,这个作用域与在同一位置声明的变量的作用域相同。异常由系统自动初始化为“未引发”条件。

从第34行开始,我们定义了三个不同的异常处理程序,它们将涵盖此过程中任何地方引发的任何异常。前两个是命名的异常处理程序,但第三个处理程序使用保留字others表示它将用于两个命名的异常处理程序未处理的任何异常。others子句是可选的,但如果包含,则必须是最后一个。

 

提出Exception

如果我们到达值为4的第28行,由于调用程序的逻辑,我们最终将检测到在到达第31行时尝试的被零除。我们不让系统生成名为Constraint_Error的异常,而是使用保留字raise后跟异常的名称来生成名为My_own_exception的异常。一旦我们引发这个异常,系统就会跳到块的末尾,查找它找到的保留字异常,然后查找具有引发的名称的异常处理程序。一旦找到它,语句就被执行,结果一条消息被输出到显示器上,我们返回到调用程序。

在这种情况下,系统不会引发异常 Constraint_Error,因为我们是在错误实际发生之前检测到错误的。您可以通过在这个过程的某处插入语句“raise constraint_Error;”来提高它,可能是在Index的值等于3时。对于您来说,将其插入到代码中是一个很好的练习,可以看到您可以引发一个系统异常以及您自己的异常。

请务必编译并执行本程序,以根据本说明验证操作是否正确,并确定您对本说明的理解。

请注意,如果发生异常,则不会更新模式out或in-out的形式参数,因为正常返回未完成。因此,部分结果将不会返回给调用程序。这里没有说明这一点,但如果需要的话,留给学生研究。

 

预定义的异常是什么?

系统可以引发四个预定义的异常,以指示非常特定的问题。下面是一个简短的定义;

1.Constraint_Error -如果某个对象超出其指定范围,则会发生此错误。

2.Program_Error-如果我们试图违反Ada控制结构,例如在没有返回的情况下从函数的底部删除,就会发生这种情况。

3.Storage_Error-如果通过递归调用或存储分配调用耗尽存储空间,则会发生这种情况。

4.Tasking_Error-当试图违反规则使用某种形式的任务分配时,会发生这种情况。

 

一个未处理的异常呢?

Example program ------> e_c17_p3.ada

 

 -- Chapter 17 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Except3 is

   procedure Divide_By_Zero(Count : INTEGER) is
      Divide_Result : INTEGER;
   begin
      Put("Count is");
      Put(Count, 3);
      Put(" and the answer is");
      Divide_Result := 25 / (Count - 4);
      Put(Divide_Result, 4);
      New_Line;
   exception
      when Constraint_Error => Put_Line(" Divide by zero occurred");
   end Divide_By_Zero;

   procedure Raise_An_Error(Count : INTEGER) is
      My_Own_Error : exception;
      Another_Result : INTEGER;
   begin
      Put("Count is");
      Put(Count, 3);
      Another_Result := 35 / (Count - 6);  -- untested divide by zero
      if Count = 3 then
         raise My_Own_Error;
      end if;
      Put_Line(" and is a legal value");
   exception
      when My_Own_Error => Put_Line(" My own error occurred");
   end Raise_An_Error;

begin
   Put_Line("Begin program here.");
   for Count in 1..7 loop
      Divide_By_Zero(Count);
      Raise_An_Error(Count);
   end loop;
   Put_Line("End of program.");

   exception
      when Constraint_Error => Put(" Constraint error detected at");
                               Put_Line(" the main program level.");
                               Put_Line("Program terminated.");
end Except3;




-- Result of Execution

-- Begin program here.
-- Count is  1 and the answer is  -8
-- Count is  1 and is a legal value
-- Count is  2 and the answer is -12
-- Count is  2 and is a legal value
-- Count is  3 and the answer is -25
-- Count is  3 My own error occurred
-- Count is  4 and the answer is Divide by zero occurred
-- Count is  4 and is a legal value
-- Count is  5 and the answer is  25
-- Count is  5 and is a legal value
-- Count is  6 and the answer is 12
-- Count is  6 Constraint error detected at the main program level.
-- Program terminated.

对名为e_c17_p3.ada的程序的检查将揭示如果引发程序未处理的异常会发生什么。总之,程序将被终止,但我们需要了解终止是如何发生的,这样我们才能智能地防止它。

主程序中有一个循环,它依次调用两个过程,Divide_By_Zero and Raise_An_Error.。第一个过程与前两个示例程序中的过程相同,唯一引发的异常是Constraint_Error,它被正确处理。

第二个过程定义了自己的异常,名为My_own_Error,它以本章前面定义的方式引发并处理它自己。第26行还有一个被零除的问题,当Count等于6时,它将引发异常Constraint_Error 。当然,定义逻辑是为了实现这一点并说明错误。

 

异常传播

当在第26行引发异常Constraint_Error 时,系统将搜索在过程末尾的第31行中找到的保留字exception 。然后,它搜索一系列语句以查找未找到的错误。由于在过程中找不到异常处理程序,因此将异常传播到调用程序的方式使异常看起来是由调用语句引发的。在这种情况下,逻辑将显示为异常Constraint_Error是由第39行中的语句引发的。再次应用异常规则,系统在块或子程序的末尾搜索异常部分,在本例中是主程序。在第43行中找到保留字exception ,系统将查找所需的异常处理程序,然后将其查找并执行,然后从主程序的底部退出并返回到操作系统。

如果没有异常处理程序,异常将被传播到操作系统,并且它将在标准输出设备上发出某种令人讨厌的消息,说明未经处理的异常导致程序终止。

显然,如果您添加了另一级子程序嵌套,您可以自己报告错误,并可能恢复程序的操作。这完全是程序定义的问题。

 

你能在不引发异常的情况下执行它吗?

如前所述,在保留字异常之后,块或子程序末尾的代码段在执行时不会引发异常。它永远不会被投入执行。

一定要编译并执行这个程序来观察异常的操作。

 

声明期间可能会发生异常

Example program ------> e_c17_p4.ada

 

-- Chapter 17 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;

procedure Except4 is

   procedure Try_It is
      VALUE : constant := 8;
      subtype LIMIT_RANGE is INTEGER range 14..33;
      Funny : LIMIT_RANGE := VALUE;
   begin
      Put_Line("We are in the Try_It procedure");
   exception
      when Constraint_Error =>
              Put_Line("Constraint error occurred");
              Put_Line("Detected in procedure Try_It");
   end Try_It;

   procedure Try_To_Fix_It is
   begin
      Put_Line("We are in the Try_To_Fix_It procedure.");
      Try_It;
   exception
      when Constraint_Error =>
              Put_Line("Constraint error occurred");
              Put_Line("Detected in procedure Try_To_Fix_It");
   end Try_To_Fix_It;


begin
   Put_Line("Begin program here.");
   for Count in 1..4 loop
      Put_Line("Ready to call Try_To_Fix_It.");
      Try_To_Fix_It;
      New_Line;
   end loop;
   Put_Line("End of program.");

   exception
      when Constraint_Error =>
              Put     ("Range error detected at the");
              Put_Line(" main program level.");
              Put_Line("Program terminated.");
end Except4;




-- Result of execution

-- Begin program here.
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- End of program.

检查名为e_c17_p4.ada的程序,以获取在程序的声明部分发生的异常的示例。

当一个过程被调用时,它的声明在逻辑被执行之前被详细阐述,正如我们之前所说的。如果其中一个声明不能正确地阐述,则会发生错误并引发异常。检查过程Try_It 将在第8行到第10行显示一个错误,其中变量Funny被声明为LIMIT_RANGE 类型,limits为14到33,然后它被初始化为值8。因为这超出了允许的范围,所以将引发异常Constraint_Error 。过程的可执行部分尚未准备好使用,因此无法使用其中定义的异常处理程序,异常将被传播到调用程序,并在其中进行处理,就像它发生在调用语句(本例中是第22行)中一样。因此,该异常将由过程Try_To_Fix_It.的第24行到第26行处理,以尝试修复它。请注意,我们从未执行名为Try_It的过程中的代码。

一定要编译并运行这个程序,然后研究结果。

 

其他预定义异常

您会发现实际上还有编译器预定义的附加异常,但这些异常都是在编译器提供的附加包中定义的。包,例如Ada.Text_IOAda.Sequential_IO, or Ada.Calendar (稍后将与tasking一起讨论),将一些异常定义为其接口的一部分,但是只有四个异常预定义为Ada的一部分。这些都在前面列出并讨论过。

 

关于例外的更多话题

Example program ------> e_c17_p5.ada

 -- Chapter 17 - Program 5
package Stuff is
   Funny_Add_Error : exception;
   procedure Add_One(Number : in out INTEGER);
   function Subtract_One(Number : INTEGER) return INTEGER;
end Stuff;



with Ada.Text_IO;
use Ada.Text_IO;

package body Stuff is

   procedure Add_One(Number : in out INTEGER) is
   begin
      Number := Number + 1;
   exception
      when Funny_Add_Error => Put_Line("Funny add error raised");
      when Constraint_Error => Put_Line("Constraint error raised");
   end Add_One;

   function Subtract_One(Number : INTEGER) return INTEGER is
      Funny_Subtract_Error : exception;
   begin
      raise Funny_Subtract_Error;
      return Number - 1;
   exception
      when Funny_Add_Error =>
                         Put_Line("Funny add error raised");
                         raise;
      when Funny_Subtract_Error =>
                         Put_Line("Funny subtract error raised");
                         raise;
   end Subtract_One;

begin
   null;
exception
-- Numeric_Error is obsolete in Ada 95.  It is another name for
--  the exception Constraint_Error.
-- when Numeric_Error =>
--                  Put_Line("Numeric error during elaboration");
   when Constraint_Error =>
                 Put_Line("Constraint_Error during elaboration");
   when Funny_Add_Error =>
                  Put_Line("Funny Add error during elaboration");
-- when Funny_Subtract_Error =>
--           Put_Line("Funny subtract error during elaboration");
end Stuff;



with Ada.Text_IO, Stuff;
use Ada.Text_IO, Stuff;

procedure Except5 is
   Index : INTEGER := 5;
   Add_Error : exception renames Stuff.Funny_Add_Error;
begin
   Add_One(Index);
   Index := Subtract_One(Index);
exception
   when Numeric_Error | Constraint_Error =>
             Put_Line("Numeric error or constraint error raised.");
   when Add_Error =>
             Put_Line("Addition error detected");
   when others =>
             Put_Line("An unknown exception raised");
             raise;      -- Raise it again for the operating system
end Except5;




-- Result of execution

-- Funny subtract error raised
-- An unknown exception raised
-- Unhandled exception; funny_subtract_error

-- (Note, The last line above will be different for each compiler,
--        but will say something about an unhandled exception.  It
--        will probably output about 10 lines of text.)

名为e_c17_p5.ada的示例程序说明了有关异常的一些附加主题,并说明了如何在包中使用异常。这是一个非常奇怪的程序,有很多异常处理的例子供你学习。您将独自研究这个程序的整体操作,但将向您指出独特的异常处理技术。

包体在第37行到第49行中包含一段初始化代码,它只由一个null语句和几个异常处理程序组成。它们仅在包的初始化期间使用,因为它们不在任何子程序的可执行部分中。您将注意到名为Funny_Add_Error的异常在包规范中声明,因此它在第46行的异常处理程序中可见,但是名为Funny_Subtract_Error 的异常在那里不可见,因为它是在函数中声明的。但是,我们很快就会看到,即使这个异常也可以传播到主程序。

当程序执行时,对函数 Subtract_One 的调用将引发异常Funny_Subtract_Error ,该异常由第32行中函数末尾的异常处理程序处理。将显示一条消息,并且第34行中的独立raise语句将引发相同的异常。此语句只会引发导致跳转到异常处理程序的异常。孤立的raise语句只能在异常处理程序中使用。异常被传递给调用程序,即使它已经在这里被处理过。

由于名为Funny_Subtract_Error 的异常对主程序不可见,因此它不能按名称处理它,但即使是此异常也可以由others子句处理,如第68行所示。打印消息后,在第70行再次引发相同的异常,并将其传递给操作系统。当您执行这个程序时,您将看到操作系统知道异常的名称。它将给您一个关于未处理异常的恶劣消息并终止操作。

 

异常处理程序中的others子句

如果使用others子句,则它必须是异常处理程序列表中的最后一个,并且不能与任何其他异常(如第64行所示)组合。与其他Ada构造一样,可以对两个或多个异常进行“or”,并使用相同的异常处理程序。Ada 83中提供了Numeric_Error ,但Ada 95中认为已过时。名称Numeric_Error Constraint_Error的同义词,它允许遗留代码无错误地编译。

请注意第59行,其中重命名了异常以缩短其名称的长度。任何异常都可以用类似的方式重命名。

一定要编译和执行这个程序,并花必要的时间来理解这里说明的异常传播。

 

什么是异常事件?

很多时候,简单地报告发生了异常是足够的,并且异常的一般性质足以允许从异常情况中恢复。但是,有时有必要提供有关异常及其原因的附加信息。为此,可以在预定义的ada95包中使用异常发生。异常事件为引发异常的一个实例提供了唯一的名称,并提供了钩子来完全分析异常发生的时间和地点。在本教程中,一个示例程序提供了最好的说明,因此请检查名为e_c17_p6.ada.的示例程序。

 

Example program ------> e_c17_p6.ada

                                       -- Chapter 17 - Program 6

with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Exceptions;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Occur is

   Except_ID : Ada.Exceptions.EXCEPTION_OCCURRENCE;

   procedure Divide_Loop is
      Divide_Result : INTEGER;
   begin

      for Index in 1..8 loop
         Put("Index is");
         Put(Index, 3);
         Put(" and the answer is");
         Divide_Result := 25 / (4 - Index);
         Put(Divide_Result, 3);
         New_Line;
      end loop;

   exception
      when Except_ID: Constraint_Error => 
          Put_Line(" Divide by zero error.");
          New_Line;
          Put_Line("  Exception name:");
          Put_Line(Ada.Exceptions.Exception_Name(Except_ID));
          New_Line;
          Put_Line("  Exception information:");
          Put_Line(Ada.Exceptions.Exception_Information(Except_ID));
          New_Line;

      when Except_ID: Storage_Error =>
          Put_Line(" Storage error detected.");
          Put_Line(Ada.Exceptions.Exception_Information(Except_ID));
          New_Line;

      when Except_ID: others =>
          Put_Line(" Unknown exception detected.");
          Put_Line(Ada.Exceptions.Exception_Information(Except_ID));
          New_Line;

   end Divide_Loop;

begin
   Put_Line("Begin program here.");
   Divide_Loop;
   Put_Line("End of program.");
end Occur;




-- Result of Execution

-- Begin program here.
-- Index is  1 and the answer is  8
-- Index is  2 and the answer is 12
-- Index is  3 and the answer is 25
-- Index is  4 and the answer is Divide by zero error.
--
--   Exception name:
-- Constraint_Error
--
--   Exception message:
-- Constraint_Error (divide by zero)
--
-- End of program.

第2行我们with 包名为 Ada.Exceptions 它为我们提供了命名和使用异常发生的能力,我们在第8行中命名了一个异常。Except_ID 可以一次引用任何单一异常,但可以在以后重用以引用任何其他异常。这个示例程序执行一个循环,其中有一个错误,一个除以零。事实上,您可以将其识别为本章第一个示例程序的修改。

当我们到达除以零条件时,会引发异常Constraint_Error,并找到第23行中定义的异常处理程序,并开始执行。但是,由于保留字when后面立即给定的出现名称,因此此特定的Constraint_Error的出现将被赋予Except_ID。一旦我们有了发生名称,我们就可以使用它检索具有异常名称的格式化消息,即使异常名称不再在作用域内。这是通过调用Exception_Name 来完成的,该名称是Ada.Exceptions package。这在第28行中说明,其中返回的文本行仅被复制到监视器。我们还可以通过调用名为Exception_Message 的函数,将异常发生的名称作为唯一参数,从而获得一个更详细的消息,包括完整的调用堆栈列表。此结果也会复制到监视器中进行检查。

实际格式依赖于实现,编译器可能会给出非常详细的文本行或非常稀疏的文本行。用于此编译的编译器发出了非常稀少的文本。

如果我们要更正错误并允许程序继续,另一个除以零的条件将导致Constraint_Error,并且可以使用 Except_ID 异常发生来分析该特定异常。显然,您可以使用此方法将某些异常记录到日志文件中,以便在执行后分析导致某些特定灾难性故障的原因。如果计划得当,这样的日志文件将在故障之前具有完整的系统操作历史记录。

 

多次使用该事件

在示例程序的第34行中,我们使用相同的发生名称检索和显示有关Storage_Error异常发生的信息。这表明异常发生的行为就像一个变量,可以用来引用程序执行期间发生的任何异常。注意,在这个程序中,Storage_Error异常很可能会引发,但这里给出的只是为了说明。

可以将相同的名称与others condition 一起使用,以覆盖发生的任何其他未处理的异常。

请确保使用编译器编译和执行此程序,以查看为您提供的输出格式。没有标准的方法来格式化这些数据,因此您的输出可能与执行结果中提供的示例输出非常不同。同样的信息将以某种有意义的方式提供给您。

 

编程练习

1.更改e_c17_p2.ada的第31行,使其在索引等于2时被零除,并确保两个异常都能正确处理。(Solution)

2.同样在e_c17_p2.ada中,在第22行声明一个新的异常,并在第29行中引发它,以查看others子句如何处理它(Solution)

 

---------------------------------------------------------------------------------------------------------------------------

原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm

翻译(百度):博客园  一个默默的 *** 的人

 

 

posted @ 2021-04-10 11:48  yangjianfeng  阅读(212)  评论(0编辑  收藏  举报