Data Hiding and Inheritance

Classes are fairly new to Fortran and we will mention only a few more of their features. Some, such as inheritance, are applicable to all types. We will describe some additional features below.

Data Hiding

One of the main purposes of OOP is to prevent outside units from doing anything without “sending a message” to an appropriate instance. As for modules, we can specify access through the PUBLIC and PRIVATE statements or attributes. As for modules, the default is PUBLIC. The previous example violates this principle. We can make everything private, which means that only members of the module can access the members of the class. We must then go through an instance of the type/class to invoke the procedures. Making a type public “exposes” the type name and its type-bound procedures, but not its variables if the module default is set to private. We will modify the example to accomplish this.

Modified Example

MODULE mytype_class
IMPLICIT NONE
   PRIVATE  !Everything contained is now private
   PUBLIC ::MyType !so need to make the type public

   TYPE MyType
      PRIVATE  !Members must still be declared private
      INTEGER   ::i,j
      REAL      ::x,y
      CONTAINS
         PROCEDURE ::init=>init_class
         PROCEDURE :: write=>write_class
   END TYPE

   CONTAINS

   SUBROUTINE init_class(self,stuff1,stuff2)
      CLASS(MyType), INTENT(INOUT) :: self
      INTEGER,       INTENT(IN)    :: i1, i2
      REAL,          INTENT(IN)    :: stuff1, stuff2
      self%i=0; self%j=0
      self%x=stuff1; self%y=stuff2
   END SUBROUTINE

   SUBROUTINE write_class(self,iunit)
      CLASS(myType), INTENT(IN) :: self
      INTEGER, INTENT(IN)       ::iunit

      WRITE(*,*) "Integers ",self%i,self%j
      WRITE(*,*) "Reals",self%x,self%y
   END SUBROUTINE

   SUBROUTINE reset(self,i1,i2,stuff1,stuff2)
      CLASS(MyType), INTENT(INOUT) :: self
      INTEGER,       INTENT(IN)    :: i1,i2
      REAL,          INTENT(IN)    :: stuff1, stuff2
      self%i=i1; self%j=i2
      self%x=stuff1; self%y=stuff2
   END SUBROUTINE

END MODULE


In the caller:

CALL myvar%init(i,j,x,y)
CALL write_class(myvar,11) ! illegal, link error
CALL myvar%write(12)       ! OK

Constructors and Destructors

A constructor is a subprogram that handles the bookkeeping to initialize an instance of a type. This may entail:

  • Assigning values to variables
  • Allocating memory for allocatable arrays
    • This never happens automatically. If an allocatable is a member of a type, a constructor must be written.

A destructor is a subprogram that releases memory for a type. This may be required if you allocate in a constructor. The garbage collection in subprograms will not release memory allocated for a type.

Fortran has no special syntax for a constructor or destructor. Programmers can define an init function or equivalent, then declare it private to be sure it can be accessed only through a type instance. Destructors can be similarly written to deallocate arrays.

Inheritance

Inheritance is when a type is derived from another type and has access to its members. Inheritance is not restricted to classes in Fortran but can be used with types as well.

type Parenttype
integer ::my_id
real    ::my_value
end typeParenttype

type, extends (Parenttype) :: Childtype
integer ::my_int
end type Childtype

Attribute Inheritance

The child type inherits all the attributes of its parent.

type(ChildType) :: billy
billy%my_id !is valid, and is equivalent to
billy%ParentType%my_id

But billy%my_int does not refer back to the parent, since that variable occurs only in the extension.

Class Inheritance

When a class is extended, not only the components but the procedures are inherited.

MODULE animal_class
   IMPLICIT NONE
   TYPE animal
      CHARACTER(LEN=7) :: my_name, species
      CONTAINS
         PROCEDURE     :: init=>init_animal
   END TYPE

   CONTAINS

      SUBROUTINE init_animal(self,my_name,species)
         CLASS(animal),     INTENT(INOUT) :: self
         CHARACTER(LEN=7), INTENT(IN)    :: my_name, species
         self%my_name=my_name
         self%species=species
      END SUBROUTINE
END MODULE

MODULE mammal_class
   USE animal_class
   IMPLICIT NONE
   TYPE, EXTENDS (animal) :: mammal
      CHARACTER(LEN=5) :: sound
      CONTAINS
         PROCEDURE :: speak=>print_sound
   END TYPE

   CONTAINS

      SUBROUTINE print_sound(self,sound)
         CLASS(mammal),    INTENT(INOUT) :: self
         CHARACTER(LEN=5), INTENT(IN)    :: sound

              self%sound=sound
              write(*,*) trim(self%my_name)," is a ",TRIM(self%species),       &
                         " and says ",trim(self%sound)//"."
      END SUBROUTINE
END MODULE

PROGRAM zoo
USE mammal_class
IMPLICIT NONE

   TYPE(mammal)    , DIMENSION(3) :: zoo_list
   CHARACTER(LEN=7), DIMENSION(3) :: names, species
   CHARACTER(LEN=5), DIMENSION(3) :: sounds
   INTEGER                         :: i

   names=["Raja   ","Leo    ","Bruno  "]
   species=["tiger  ","lion   ","bear   "]
   sounds=["chuff","roar ","growl"]

   DO i=1,SIZE(names)
      call zoo_list(i)%init(names(i),species(i))
   ENDDO

   DO i=1,SIZE(names)
      call zoo_list(i)%speak(sounds(i))
   ENDDO

END PROGRAM

Exercise

  • Write a class Atom that contains the following attributes:
    • Element symbol
    • Element name
    • Isotopic mass (mass of a single isotope, not the “atomic weight” averaged over a mix of isotopes)
    • Atomic number
  • The method should be
    • Compute and return the number of neutrons from the mass and number (n=mass-number)
Previous
Next