Ada 95 Quality and Style Guide Chapter 9

Chapter 9: Object-Oriented Features - TOC - 9.2 TAGGED TYPE HIERARCHIES

9.2.3 Controlled Types

guideline

  • Consider using a controlled type whenever a type allocates resources that must be deallocated or otherwise "cleaned up" on destruction or overwriting.
  • Use a derivation from a controlled type in preference to providing an explicit "cleanup" operation that must be called by clients of the type.
  • When overriding the adjustment and finalization procedures derived from controlled types, define the finalization procedure to undo the effects of the adjustment procedure.
  • Derived type initialization procedures should call the initialization procedure of their parent as part of their type-specific initialization.
  • Derived type finalization procedures should call the finalization procedure of their parent as part of their type-specific finalization.
  • Consider deriving a data structure's components rather than the enclosing data structure from a controlled type.

  • example

    The following example demonstrates the use of controlled types in the implementation of a simple linked list. Because the Linked_List type is derived from Ada.Finalization.Controlled, the Finalize procedure will be called automatically when objects of the Linked_List type complete their scope of execution:

    with Ada.Finalization;
    package Linked_List_Package is
       type Iterator is private;
       type Data_Type is ...
       type Linked_List is new Ada.Finalization.Controlled with private;
       function Head (List : Linked_List) return Iterator;
       procedure Get_Next (Element  : in out Iterator;
                           Data     :    out Data_Type);
       procedure Add (List     : in out Linked_List;
                      New_Data : in     Data_Type);
       procedure Finalize (List : in out Linked_List); -- reset Linked_List structure
       -- Initialize and Adjust are left to the default implementation.
    private
       type Node;
       type Node_Ptr is access Node;
       type Node is
          record
             Data : Data_Type;
             Next : Node_Ptr;
          end record;
       type Iterator is new Node_Ptr;
       type Linked_List is new Ada.Finalization.Controlled with
          record
             Number_Of_Items : Natural := 0;
             Root            : Node_Ptr;
          end record;
    end Linked_List_Package;
    --------------------------------------------------------------------------
    package body Linked_List_Package is
    
       function Head (List : Linked_List) return Iterator is
          Head_Node_Ptr : Iterator;
       begin
          Head_Node_Ptr := Iterator (List.Root);
          return Head_Node_Ptr;  -- Return the head element of the list
       end Head;
    
       procedure Get_Next (Element : in out Iterator;
                           Data    :    out Data_Type) is
       begin
          --
          -- Given an element, return the next element (or null)
          --
       end Get_Next;
    
       procedure Add (List     : in out Linked_List;
                      New_Data : in     Data_Type) is
       begin
          --
          -- Add a new element to the head of the list
          --
       end Add;
    
       procedure Finalize (List : in out Linked_List) is
       begin
          -- Release all storage used by the linked list
          --   and reinitialize.
       end Finalize;
    
    end Linked_List_Package;
    

    rationale

    The three controlling operations, Initialize, Adjust, and Finalize, serve as automatically called procedures that control three primitive activities in the life of an object (Ada Reference Manual 1995, §7.6). When an assignment to an object of a type derived from Controlled occurs, adjustment and finalization work in tandem. Finalization cleans up the object being overwritten (e.g., reclaims heap space), then adjustment finishes the assignment work once the value being assigned has been copied (e.g., to implement a deep copy).

    You can ensure that the derived type's initialization is consistent with that of the parent by calling the parent type's initialization from the derived type's initialization.

    You can ensure that the derived type's finalization is consistent with that of the parent by calling the parent type's finalization from the derived type's finalization.

    In general, you should call parent initialization before descendant-specific initialization. Similarly, you should call parent finalization after descendant-specific finalization. (You may position the parent initialization and/or finalization at the beginning or end of the procedure.)


    < Previous Page Search Contents Index Next Page >
    1 2 3 4 5 6 7 8 9 10 11
    TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC
    Appendix References Bibliography