Ada 95 Quality and Style Guide Chapter 9

Chapter 9: Object-Oriented Features - TOC - 9.3 TAGGED TYPE OPERATIONS

9.3.3 Constructors

Ada does not define a unique syntax for constructors. In Ada a constructor for a type is defined as an operation that produces as a result a constructed object, i.e., an initialized instance of the type.

guideline

  • Avoid declaring a constructor as a primitive abstract operation.
  • Use a primitive abstract operation to declare an initialization function or constructor only when objects of the inheriting derived types will not require additional parameters for initialization.
  • Consider using access discriminants to provide parameters to default initialization.
  • Use constructors for explicit initialization.
  • Consider splitting the initialization and construction of an object.
  • Consider declaring a constructor operation in a child package.
  • Consider declaring a constructor operation to return an access value to the constructed object (Dewar 1995).

  • example

    The following example illustrates the declaration of a constructor in a child package:

    --------------------------------------------------------------------------
    package Game is
       type Game_Piece is tagged ...
       ...
    
    end Game;
    --------------------------------------------------------------------------
    package Game.Constructors is
       function Make_Piece return Game_Piece;
       ...
    end Game.Constructors;
    --------------------------------------------------------------------------
    
    

    The following example shows how to split the initialization and construction of an object:

    type Vehicle is tagged ...
    
    procedure Initialize (Self : in out Vehicle;
                          Make : in     String);
    
    ...
    
    type Car is new Vehicle with ... ;
    type Car_Ptr is access all Car'Class;
    
    ...
    
    procedure Initialize (Self  : in out Car_Ptr;
                          Make  : in     String;
                          Model : in     String) is
    begin -- Initialize
       Initialize (Vehicle (Self.all), Make);
       ...
       -- initialization of Car
    end Initialize;
    
    function Create (Make  : in String;
                     Model : in String) return Car_Ptr is
       Temp_Ptr : Car_Ptr;
    begin -- Create
       Temp_Ptr := new Car;
       Initialize (Temp_Ptr, Make, Model);
       return Temp_Ptr;
    end Create;
    
    

    rationale

    Constructor operations for the types in a type hierarchy (assuming tagged types and their derivatives) usually differ in their parameter profiles. The constructor will typically need more parameters because of the added components in the descendant types. You run into a problem when you let constructor operations be inherited because you now have operations for which there is no meaningful implementation (default or overridden). Effectively, you violate the class-wide properties (see Guideline 9.2.1) because the root constructor will not successfully construct a descendant object. Inherited operations cannot add parameters to their parameter profile, so these are inappropriate to use as constructors.

    You cannot initialize a limited type at its declaration, so you may need to use an access discriminant and rely on default initialization. For a tagged type, however, you should not assume that any default initialization is sufficient, and you should declare constructors. For limited types, the constructors must be separate procedures or functions that return an access to the limited type.

    The example shows using a constructor in a child package. By declaring constructor operations in either a child package or a nested package, you avoid the problems associated with making them primitive operations. Because they are no longer primitive operations, they cannot be inherited. By declaring them in a child package (see also Guidelines 4.1.6 and 4.2.2 on using child packages versus nested packages), you gain the ability to change them without affecting the clients of the parent package (Taft 1995b).

    You should put the construction logic and initialization logic in distinct subprograms so that you are able to call the initialization routine for the parent tagged type.

    notes

    When you extend a tagged type (regardless whether it is an abstract type), you can choose to declare as abstract some of the additional operations. Doing so, however, means that the derived type must also be declared as abstract. If this newly derived type has inherited any functions that name it as the return type, these inherited functions now also become abstract (Barnes 1996). If one of these primitive functions served as the constructor function, you have now violated the first guideline in that the constructor has become a primitive abstract operation.


    < 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