Fortran: Fortran 95: Module und OOP

<<< zur Fortran-Startseite
<< Fortran 95 Fortran 2003 >>
< Systemroutinen Offizielle Fortran 95-Erweiterungen >

Module im Detail

Bearbeiten

Die Bezeichnung des Schlüsselworts module weist schon darauf hin, dass Fortran 90/95 eine modulare Softwareentwicklung ermöglicht.

Modulare Programmierung

Bearbeiten

Was ist modulare Programmierung?   Modulare Programmierung


Das Modul-Konzept in Fortran 90/95 unterstützt diesen Ansatz vollständig. Das module-Konstrukt gliedert sich schematisch so

module ...

Datenbereich
Methodenbereich

end module ...

Vor dem Datenbereich können noch einige Deklarationen (implicit none, save, etc.) eingefügt sein. Der Methodenbereich wird durch das Schlüsselwort contains angekündigt oder als Interface deklariert. Aber prinzipiell gilt, dass zusammengehörende Daten und die dazugehörenden Methoden (Unterprogramme) in einem Modul zusammengefasst werden.

Zugriffsteuerung

Bearbeiten

Durch Angabe des Schlüsselworts private lässt sich die Sichtbarkeit von Datenelementen einschränken. Auf solcherart deklarierte Variablen läßt sich außerhalb des Moduls nicht zugreifen. public erlaubt den Zugriff auf entsprechend deklarierte Variablen auch von außerhalb. Zweiteres ist Standardverhalten und das Schlüsselwort public muss somit nicht explizit angegeben werden.

Beispiel:

Fortran 90/95-Code (free source form)
module mod_bsp
  implicit none
  save

! Datenbereich  
  real, private :: x = 1.2
  real          :: y = 9.8
  
  contains
! Methodenbereich
    real function addX (a)
      real, intent (in) :: a

      addX = x + a    
    end function addX
end module mod_bsp


program bsp
  use mod_bsp
  implicit none
  
  write (*,*) "Ergebnis1 = ", addX (2.1)
  write (*,*) "Ergebnis2 = ", y + 2.1

! Ausgabe:  
!    Ergebnis1 =    3.300000
!    Ergebnis2 =    11.90000
  
! Folgendes geht nicht -> Fehlermeldung:
! write (*,*) "Ergebnis3 = ", x + 2.1 
!   1)  x ist private und somit außerhalb des Moduls nicht bekannt
!   2) auch im Hauptprogramm selbst ist kein x deklariert (implicit none,
!      es wäre ohnehin nicht die gleiche Variable wie im Modul mod_bsp)
end program bsp


Rein formal läßt sich der Zugriff auf die Daten und/oder Methoden eines Moduls (module procedures) auch auf andere Arten einschränken, z.B.

module mod_bsp
  implicit none
  save
 
  private :: x  ! , .... 

  real :: x = 1.2, y = 9.8
! ...
 

oder

module mod_bsp
  implicit none
  save
 
  private 
  public :: y, addX  ! , ...

  real :: x = 1.2, y = 9.8
! ...

Datenkapselung, COMMONS-Ersatz: Module als Datenbereich

Bearbeiten
 

Module als reinen Datenbereich zu nutzen ist vor allem dann interessant, wenn aus mehreren Programmeinheiten auf die gleichen Daten zugegriffen werden muss, jedoch die zugreifenden Unterprogramme nicht wirklich modulspezifisch sind und dementsprechend nicht als Bestandteil des Moduls angelegt werden.

Beispiel:

Fortran 90/95-Code (free source form)
module konstanten
  real, parameter :: PI = 3.141593
  real, parameter :: E =  2.718282
end module konstanten
 
 
! Hauptprogramm 
program bsp
  use konstanten
  implicit none 
  
  real :: kreisflaeche
 
  write(*,*) 'PI = ', PI
  write(*,*) 'E = ',  E
  write(*,*) 'Kreisflaeche fuer r=2.1 = ', kreisflaeche(2.1)
  call calcPiMalE  
end program bsp
 
 
! Unterprogramm 1 
real function kreisflaeche(r)
  use konstanten
  implicit none

  real, intent (in) :: r 

  kreisflaeche = r**2 * PI   
end function kreisflaeche


! Unterprogramm 2
subroutine calcPiMalE()
  use konstanten
  implicit none

  write (*,*) 'PI * E = ', PI * E 
end subroutine calcPiMalE

! Ausgabe:
!   PI =    3.141593
!   E =    2.718282
!   Kreisflaeche fuer r=2.1 =    13.85442
!   PI * E =    8.539735

Datenabstraktion: Zusammenfassung von Daten und Methoden in einem Modul

Bearbeiten
 

Sind Daten und Unterprogramme als zusammengehörend zu betrachten, so ist es sinnvoll diese auch gemeinsam in einem Modul abzulegen. Ein Vorteil dabei ist, dass somit die Zugriffssteuerung gezielt eingesetzt werden kann. Moduldaten, die nicht von außerhalb des Moduls geändert werden dürfen, werden als private deklariert. Der Zugriff auf diese Daten kann dann nur noch mittels Methoden des Moduls erfolgen.

Beispiel:

Fortran 90/95-Code (free source form)
module kreis
  implicit none
  save

  real, parameter :: PI = 3.141593
  real, private   :: r  = 0.0
  
  contains
    subroutine setR(val)
      real, intent(in) :: val
      r = val
    end subroutine setR   
 
    real function getR()
      getR = r
    end function getR

    real function kreisflaeche()
      kreisflaeche = r**2 * PI
    end function kreisflaeche
end module kreis
 
 
! Hauptprogramm 
program bsp
  use kreis
  implicit none 
  
  call setR(5.0)
  write (*,*) "r = ", getR() 
  write (*,*) "Flaeche = ", kreisflaeche() 

  call sub1(5.0)
end program bsp
 
 
! Unterprogramm
subroutine sub1(val)
  use kreis
  implicit none

  real, intent(in) :: val
  
  call setR(val*2.56) 
  write (*,*) "r = ", getR() 
  write (*,*) "Flaeche = ", kreisflaeche()
end subroutine sub1

! Ausgabe:
!   r =  5.
!   Flaeche =  78.539825
!   r =  12.799999
!   Flaeche =  514.7185

Modul und Datenverbund

Bearbeiten
 

Das Schlüsselwort private darf in einem Datenverbund nur in Verbindung mit einem Modul Anwendung finden. Einerseits kann die Sichtbarkeit aller Variablen im Datenverbund eingeschränkt werden. Anderseits kann auch der Datenverbund selbst als private deklariert werden. Ein Zugriff auf derart deklarierte Datenelemente ist dann nur noch durch Unterprogramme des gleichen Moduls möglich. Standardmäßig sind sowohl Datenverbund als auch seine Einzelkomponenten, so wie alle anderen nicht zugriffsbeschränkten Datenelemente, in einem Modul öffentlich (public).


Beispiel: Öffentlicher Datenverbund

Fortran 90/95-Code (free source form)
module mod1
  save

  type :: tripel
    real:: x, y, z
  end type tripel  
end module mod1

program bsp
  use mod1
  implicit none
 
  type(tripel) :: tr = tripel(10.5, 0.0, -6.5)
 
  write(*, *) tr
! Ausgabe:
!   10.5   0.0   -6.5
end program bsp


Beispiel: Öffentlicher Datenverbund mit privaten Datenelementen

Fortran 90/95-Code (free source form)
module mod1
  save

  type :: tripel
    private
    real:: x, y, z
  end type tripel  
  
  
  contains
    subroutine createTripel(this, valX, valY, valZ)
      type(tripel)     :: this
      real, intent(in) :: valX, valY, valZ
    
      this%x = valX
      this%y = valY
      this%z = valZ                  
    end subroutine createTripel 
      
            
    subroutine writeTripel(this)
      type(tripel) :: this    
      
      write(*,*) this
    end subroutine writeTripel 
end module mod1


program bsp
  use mod1
  implicit none
 
  type(tripel) :: tr 
  
  call createTripel(tr, 10.5, 0.0, -6.5)
  call writeTripel(tr)
! Ausgabe:
!   10.50000       0.000000      -6.500000
end program bsp


Beispiel: Privater Datenverbund

Fortran 90/95-Code (free source form)
module mod1
  save

  type, private :: tripel
    real :: x = 0.0, y=0.0, z=0.0
  end type tripel
  
  type(tripel), private :: t
  
  contains    
    subroutine changeTripel(valX, valY, valZ)
      real, intent(in) :: valX, valY, valZ
      t = tripel(valX, valY, valZ)      
    end subroutine changeTripel

    subroutine writeTripel()
      write (*,*) t
    end subroutine writeTripel
end module mod1


program bsp
  use mod1
  implicit none

! Hier könnte z.B. keine Variable vom Typ "triple" angelegt werden, da in dieser PE nicht 
! sichtbar (privater Datenverbund des Moduls mod1)

  call writeTripel
! Ausgabe:
!    0.0 0.0 0.0

  call changeTripel(10.5, -5.0, -3.5)

  call writeTripel
! Ausgabe:
!    10.5 -5. -3.5

  call unterprogramm
! Ausgabe:
!    10.5 -5. -3.5   
end program bsp


subroutine unterprogramm
  use mod1

  call writeTripel
end subroutine unterprogramm

Dieses Beispiel scheint auf den ersten Blick im Gegensatz zu den beiden vorherigen Varianten unnötige Einschränkungen aufzuweisen. Im Haupt- und Unterprogramm können keine Variablen des Typs tripel angelegt werden. Im Modul wird immer auf die gleiche Variable t zugegriffen. Das gewählte Beispiel könnte somit als Singleton beschrieben werden. Es ist außerhalb des Moduls sichergestellt, dass immer nur ein Tripel existiert. Singletons werden auch in der objektorientierten Softwareentwicklung verwendet. Daneben sind auch andere Situationen vorstellbar, in denen sich das Konzept eines privaten Datenverbunds als nützlich erweisen kann.

Die Schnittstelle: Das Modul als Unterprogrammbibliothek

Bearbeiten
 

Ein Modul kann als Sammelstelle für Unterprogramme dienen - quasi eine Bibliothek für häufig und in verschiedenen Kontexten benötigte Prozeduren.

Vorteile:

  • Ordnung
  • Modul kann in eine eigenständige Datei ausgelagert werden.
  • Parameter-Datentypüberprüfung.


Beispiel: Parameter-Datentypüberprüfung

ohne Modul mit Modul
program bsp
  implicit none
  
  integer :: zahl = 5

  
  call unterprogramm(zahl)
end program bsp


subroutine unterprogramm(a)
  implicit none
  
  real, intent(in) :: a
  
  write (*,*) a  
end subroutine unterprogramm

module test
  contains
    subroutine unterprogramm(a)
      implicit none
  
      real, intent(in) :: a
  
      write (*,*) a  
    end subroutine unterprogramm
end module test


program bsp
  use test
  implicit none
  
  integer :: zahl = 5

  
  call unterprogramm(zahl)
end program bsp

  • gfortran und ifort compilieren dieses Programm ohne Warnhinweis.
  • Der g95 liefert in diesem Fall zumindest eine Warnung, compiliert jedoch auch das Programm.

Die Datenausgabe zur Programmlaufzeit liefert jeweils einen fehlerhaften Wert.

Bereits beim Compilieren tritt eine Fehlermeldung auf, z.B. bei g95:

In file ....f90:20

  call unterprogramm(zahl)
                         1
Error: Type mismatch in parameter 'a' at (1). Passing INTEGER(4) to REAL(4)

Das Programm lässt sich so nicht compilieren.

Der Schnittstellenblock: interface

Bearbeiten

Fortran 90/95 kennt das Sprachkonstrukt interface, welches sich in vielerlei Hinsicht nutzbringend verwenden läßt. Schnittstellenblöcke sind modulunabhängig verwendbar.

interface [name]
  [interface-Spezifikationsteil]
end interface [name]

Der interface-Spezifikationsteil beinhaltet nur diejenigen Informationen, die für die Deklaration der Schnittstelle relevant sind (z.B. Unterprogrammbezeichnung, Variablendeklaration). Die genaue Festlegung der Unterprogramme (inkl. Ausführungsteil) erfolgt dann außerhalb des Schnittstellenblocks. Ein wesentliches Merkmal des Schnittstellenblocks ist die penible Überprüfung der Unterprogrammparameterdatentypen, so wie das auch beim Einsatz von Modulen geschieht.


Beispiel: Parameter-Datentypüberprüfung

ohne Schnittstellenblock mit Schnittstellenblock
program bsp
  implicit none
  
  integer :: zahl = 5
 
  call unterprogramm(zahl)
end program bsp


subroutine unterprogramm(a)
  implicit none
  
  real, intent(in) :: a
  
  write (*,*) a  
end subroutine unterprogramm
program bsp
  implicit none
  
  integer :: zahl = 5
  
  interface
    subroutine unterprogramm(a)
      real, intent(in) :: a
    end subroutine unterprogramm    
  end interface
  
  call unterprogramm(zahl)
end program bsp


subroutine unterprogramm(a)
  implicit none
  
  real, intent(in) :: a
  
  write (*,*) a  
end subroutine unterprogramm
  • gfortran und ifort compilieren dieses Programm ohne Warnhinweis.
  • Der g95 liefert in diesem Fall zumindest eine Warnung, compiliert jedoch auch das Programm.

Die Datenausgabe zur Programmlaufzeit liefert jeweils einen fehlerhaften Wert.

Bereits beim Compilieren tritt eine Fehlermeldung auf, z.B. bei gfortran:

In file ....f90:15

call unterprogramm(zahl)
                   1
Error: Type/rank mismatch in argument 'a' at (1)

Das Programm lässt sich so nicht compilieren.

Generische Unterprogrammschnittstelle
Bearbeiten

Fortran 90/95 ermöglicht generische Methoden. Die im Schnittstellenblock deklarierten Unterprogramme können damit über den gleichen Unterprogrammnamen aufgerufen werden. Intern erfolgt dann der Aufruf des jeweils passenden Unterprogramms durch die Unterschiede der Datentypen der Unterprogrammparameter. Mittel zum Zweck ist der benannte Schnittstellenblock.

Beispiel:

Fortran 90/95-Code (free source form)
program bsp
  implicit none
  
  interface gensub
    subroutine writeReal(val)
      real, intent(in) ::val
    end subroutine writeReal 

    subroutine writeInteger(val)
      integer, intent(in) ::val
    end subroutine writeInteger

    subroutine writeCharacter(val)
      character, intent(in) :: val
    end subroutine writeCharacter   
  end interface gensub
 
  call gensub(5.5)
  call gensub(3)
  call gensub("H")
  call writeCharacter("X") 

! Ausgabe:
!   Real-Wert =    5.500000
!   Integer-Wert =            3
!   Zeichen = H
!   Zeichen = X
end program bsp


subroutine writeReal(val)
  real, intent(in) ::val
  write (*,*) "Real-Wert = ", val
end subroutine writeReal 

subroutine writeInteger(val)
  integer, intent(in) ::val
  write (*,*) "Integer-Wert = ", val
end subroutine writeInteger

subroutine writeCharacter(val)
  character, intent(in) ::val
  write (*,*) "Zeichen = ", val
end subroutine writeCharacter
Operatorüberladung
Bearbeiten

Fortran 90/95 verwendet von Haus aus Operatorüberladung. So können z.B. Variablen arithmetischen Datentyps einfach miteinander addiert werden:

c = a + b

Dieser Ausdruck funktioniert unabhängig davon, ob die Variablen vom Typ Ganzzahl oder Gleitkommzahl sind. Und auch für Felder funktioniert diese einfache Form der Addition. Die einzelnen Feldkomponenten werden korrekt addiert. Das ist in anderen Programmiersprachen nicht selbstverständlich. Zusätzlich kann in Fortran 90/95 auch der Programmierer mit Hilfe von Schnittstellenblöcken sogenannte defined operations festlegen.

Beispiel:

Fortran 90/95-Code (free source form)
program bsp
  implicit none

  interface operator (.PLUS.)
    function charAdd(c1, c2)
      character, intent(in) :: c1, c2
      character(len=2)      :: charAdd
    end function charAdd
  end interface operator (.PLUS.)

! "Addition" mittels definiertem .PLUS.-Operator  
  write (*,*) "c1 .PLUS. c2 = ", "A" .PLUS. "B"

! oder auch mittels Funktion
  write (*,*) "charAdd () = ", charAdd("A", "B")

! Ausgabe:
!    c1 .PLUS. c2 = AB
!    charAdd () = AB   
end program bsp


function charAdd(c1, c2)
  implicit none

  character, intent(in) :: c1, c2
  character(len=2)      :: charAdd

  charAdd = c1 // c2     
end function charAdd

Beispiel: nur mit gfortran compilierbar, nicht mit g95 oder ifort

Fortran 90/95-Code (free source form)
module mod
  type :: tripel
    real x, y, z
  end type
end module mod

program bsp
  use mod
  implicit none
   

  interface operator (*)
    function tripelMult(t1, t2)
      type(tripel), intent(in) :: t1, t2
      type(tripel)             :: tripelMult
    end function tripelMult
  end interface operator (*)

! Multiplikation mittels überladenem *-Operator  
  write (*,*) "t1 * t2 =", tripel(2.0, 3.0, 4.0) * tripel(1.5, 0.5, 2.0)

! oder auch mittels Funktion
  write (*,*) "tripelMult () =", tripelMult(tripel(2.0, 3.0, 4.0), tripel(1.5, 0.5, 2.0))  

! Ausgabe:
!    t1 * t2 =   3.000000       1.500000       8.000000
!    tripelMult () =   3.000000       1.500000       8.000000
end program bsp


function tripelMult(t1, t2)
  use mod
  implicit none

  type(tripel), intent(in) :: t1, t2
  type(tripel)             :: tripelMult
  
  tripelMult = tripel(t1%x * t2%x, t1%y*t2%y, t1%z*t2%z)  
end function tripelMult


Bei der Verwendung von defined operations sind jedoch einige Bedingungen zu beachten.

Neben den defined operations (interface operator) gibt es auch noch defined assignments (interface assignment) für die Überladung des Zuweisungsoperators.

Schnittstellenblock und Modul

Bearbeiten

Beim vorigen Beispiel bestand das Problem, dass nur einer der getesteten Compiler eine ausführbare Datei zu Stande brachte. Abhilfe schaffen kann die Verlagerung des Schnittstellenblocks in das Modul unter Zuhilfenahme von module procedure.


Beispiel: Diesen Programmcode schlucken sowohl gfortran, g95 als auch ifort problemlos

Fortran 90/95-Code (free source form)
module mod
  type :: tripel
    real x, y, z
  end type
  
  interface operator (*)
    module procedure tripelMult
  end interface operator (*)
  
  contains
    function tripelMult(t1, t2)
      implicit none

      type(tripel), intent(in) :: t1, t2
      type(tripel)             :: tripelMult
  
      tripelMult = tripel(t1%x * t2%x, t1%y*t2%y, t1%z*t2%z)  
    end function tripelMult
end module mod

program bsp
  use mod
  implicit none

! Multiplikation mittels überladenem *-Operator  
  write (*,*) "t1 * t2 =", tripel(2.0, 3.0, 4.0) * tripel(1.5, 0.5, 2.0)

! oder auch mittels Funktion
  write (*,*) "tripelMult () =", tripelMult(tripel(2.0, 3.0, 4.0), tripel(1.5, 0.5, 2.0))  

! Ausgabe:
!    t1 * t2 =   3.000000       1.500000       8.000000
!    tripelMult () =   3.000000       1.500000       8.000000
end program bsp


Unterprogramme: Die Übergabe optionaler und benannter Parameter

Bearbeiten

Der Einsatz optionaler Parameter wurde bereits im Kapitel Unterprogramme abgehandelt. Zusätzlich unterstützt Fortran 90/95 auch die Verwendung von benannten Parametern (argument keywords). Dazu ist es erforderlich, dass das Unterprogramm ein explizites Interface aufweist, wie das z.B. bei Einbindung von Unterprogrammen in Module automatisch der Fall ist.

Beispiel:

Fortran 90/95-Code (free source form)
module m1
  contains
    subroutine abc( var1, var2 )
      implicit none
  
      integer, intent( in ) :: var1, var2
  
      write( *, * ) var1, var2
    end subroutine abc
end module m1


program bsp
  use m1
  implicit none

  call abc( 12, 99 )
  call abc( var2 = 99, var1 = 12 )
  call abc( var2 = 12, var1 = 99 )
  call abc( 12, var2 = 99 )
! call abc( var1 = 12, 99 )    ! so funktioniert das nicht

! Ausgabe:
!     12          99
!     12          99
!     99          12
!     12          99
end program bsp

Sonstiges

Bearbeiten
  • use modulbezeichnung, only : modulelemente
  • use modulbezeichnung => bezeichnung

Objektorientierte Programmierung

Bearbeiten

Modulares Programmieren reißt heutzutage niemanden mehr vom Hocker und selbst objektorientierte Konzepte sind seit spätestens Ende der 80-Jahre des vergangenen Jahrhunderts Stand der Technik. Im akademischen Bereich waren diese Dinge natürlich schon früher bekannt. Fortran hat in diesem Bereich also nie eine Vorreiterrolle inne gehabt. Nach jeweils kurzen Nachdenkphasen haben aber die Fortran-Leute unerschrocken auch in diesem Bereich nachgezogen.

Fortran bildet also aufgrund seiner langen Geschichte ein weites Spektrum der Programmierparadigmen ab: vom prozeduralen Programmieren (FORTRAN 77) über das modulare Programmieren (Fortran 90/95) hin zum objektorientierten Programmieren (Fortran 2003).

Fortran 90/95 ist keine typisch objektorientierte Sprache. Trotzdem lässt sich mit den modularen Spracheigenschaften von Fortran 90/95 bereits in weiten Bereichen Objektorientierung simulieren beziehungsweise nachbauen. Aber erst mit Fortran 2003 lässt sich wirklich komfortabel objektorientiert programmieren. Diese Fortran-Version wirbt dann auch „offiziell“ mit diesem Merkmal.

Welche Eigenschaften verbindet man typischerweise mit Objektorientierung? Da wären

  1. Klassen - Datenkapselung und Datenabstraktion
  2. Objekte
  3. Vererbung
  4. Statische Polymorphie: Überladen von Funktionen (und Operatoren)
  5. Run-Time-Polymorphie

Welche dieser Merkmale bietet Fortran 90/95 von Haus aus und was läßt sich relativ einfach nachbauen?

Klassen - Datenkapselung und Datenabstraktion

Bearbeiten

Was ist eine Klasse?


Ein Fortran 90/95-Modul sieht sehr ähnlich aus wie eine Klasse in der objektorientierten Programmierung und ist dementsprechend auch Ausgangspunkt für den objektorientierten Ansatz in Fortran 90/95.

Klasse Fortran 90/95
Klassenbezeichner
Datenbereich (Attribute)
Methodenbereich (Operationen)
module ...
 ! Datenbereich

  contains
  ! Methodenbereich
end module ...

Einen grundlegenden Mechanismus zum Schreiben objektorientierter Programme stellt Fortran 90/95 somit in Form der Module zur Verfügung. Jetzt stellt sich aber die Frage, wie diese Klasse instantiiert werden soll. Das wird im Abschnitt Objekte gezeigt.

Was sind Objekte?   Objekt (Programmierung)


Ein einfaches Modul mit ein paar konventionellen Datenelementen und Prozeduren ist noch keine Klasse, aus der Objekte zu erzeugen wären. Dazu bedarf es noch eines Unterscheidungskriteriums zwischen den einzelnen Objekten. Dieses Unterscheidungskriterium kann, wie auch schon früher im Teilkapitel Modul und Datenverbund gezeigt, mittels Datenverbund hergestellt werden. Die Objekte werden somit nicht über den Modulnamen als Klasse angesprochen, sondern über die Datenverbund-Bezeichnung. Auch das ist keine neue Erkenntnis, sondern eine geschickte Ausnutzung eines Mechanismus, den Fortran 90/95 standardmäßig mit Modulen und Datenverbund zur Verfügung stellt.


Beispiel:

 


Fortran 90/95-Code (free source form)
module tripelClass
  implicit none
  save  

  type :: tripel
    real:: x, y, z
  end type tripel  
  
  contains
    subroutine write(this)
      type(tripel), intent(in) :: this
    
      write(*, *) "********** Tripel **********"
      write(*, *) this
    end subroutine
    
end module tripelClass


program bsp
  use tripelClass
  implicit none
 
  type(tripel) :: obj1 = tripel(1.5, 0.0, -6.5)
  type(tripel) :: obj2 = tripel(2.5, 1.0, -4.5)
  type(tripel) :: obj3 = tripel(3.5, 2.0, -2.5)
 
  call write(obj1)
  call write(obj2)
  call write(obj3)
  
! Ausgabe:
!   ********** Tripel **********
!      1.500000       0.000000      -6.500000
! ********** Tripel **********
!      2.500000       1.000000      -4.500000
! ********** Tripel **********
!      3.500000       2.000000      -2.500000
end program bsp


Weitere wichtige Elemente beim Erzeugen und Zerstören von Objekten sind in der OOP die sogenannten Konstruktoren und Destruktoren.

Was sind Konstruktoren und Destruktoren?   Konstruktoren und Destruktoren

Konstruktoren und Destruktoren kennt Fortran 90/95 natürlich nicht. Im Bedarfsfall sind diese Elemente also mittels konventioneller Unterprogramme nachzubilden und dann jeweils explizit in den entsprechenden Programmabschnitten, nach Erzeugung beziehungsweise beim Abbau des jeweiligen Objektes, manuell aufzurufen.

Vererbung

Bearbeiten

Was ist Vererbung?   Vererbung (Programmierung)


Das OOP-Prinzip „Vererbung“ ist in Fortran 90/95 nur über Umwege realisierbar. Fortran 90/95 kennt konzeptionsbedingt für diesen Zweck keinen einfachen programmtechnischen Mechanismus. Nachfolgend wird anhand eines kleinen und überschaubaren Beispiels eine mögliche Lösung demonstriert.


Beispiel:

 


Fortran 90/95-Code (free source form)
module tripelClass
  implicit none
  save  

  type :: tripel
    real:: x, y, z
  end type tripel  
  
  contains
    subroutine write(this)
      type(tripel), intent(in) :: this
    
      write(*, *) "********** Tripel **********"
      write(*, *) this
    end subroutine
end module tripelClass


module coordClass
  use tripelClass
  implicit none 
  save
  
  type :: coord
    type(tripel) :: tr
    integer      :: id  
  end type  
  
  contains
    subroutine constructCoord(this, t, i)
      implicit none
      
      type(coord), intent(out) :: this
      type(tripel), intent(in) :: t
      integer                  :: i
      
      this%tr = t
      this%id = i
    end subroutine constructCoord
  
  
    subroutine writeCoord(this)
      implicit none
      
      type(coord), intent(in) :: this

      write(*, "(A, I5, A)") "******************* KOORDINATE ", this%id, " *******************"
      call write(this%tr)
    end subroutine writeCoord
end module coordClass


program bsp
  use coordClass
  implicit none
 
  type(coord) :: obj1 = coord(tripel(1.5, 0.0, -6.5), 1005)
  type(coord) :: obj2 = coord(tripel(2.5, 1.0, -4.5), 1006)
  type(coord) :: obj3 = coord(tripel(3.5, 2.0, -2.5), 1007)
 
  call writeCoord(obj1)
  call writeCoord(obj2)
  call writeCoord(obj3)
  
! Ausgabe:
!   ******************* KOORDINATE  1005 *******************
!    ********** Tripel **********
!      1.500000       0.000000      -6.500000
!   ******************* KOORDINATE  1006 *******************
!    ********** Tripel **********
!      2.500000       1.000000      -4.500000
!   ******************* KOORDINATE  1007 *******************
!    ********** Tripel **********
!      3.500000       2.000000      -2.500000
end program bsp

Statische Polymorphie: Überladen von Funktionen (und Operatoren)

Bearbeiten

Das Überladen von Funktionen und Operatoren unterstützt Fortran 90/95 wiederum standardmäßig. Diese Konzepte wurden schon in den Abschnitten

kurz erläutert.

Run-Time-Polymorphie

Bearbeiten

Polymorphie zur Laufzeit eines Programmes ist mit den Mitteln von Fortran 90/95 nur einigermaßen aufwendig und kompliziert nachzubauen, jedoch prinzipiell möglich. Es sei zu diesem Thema auf die weiterführende Literatur verwiesen.

Ausblick

Bearbeiten

Sind die Methoden zur Nachbildung objektorientierter Mechanismen auch in Fortan 2003 verwendbar? Prinzipiell schon, jedoch bietet Fortran 2003 bessere und erweiterte Möglichkeiten zur Behandlung dieses Themas. Dort wurde nämlich das Prinzip des Datenverbunds wesentlich erweitert. Ein type-Block kann dann neben den Datenelementen auch Unterprogramme beinhalten, das Schlüsselwort extends zwecks Vererbungmechanismus ist vorhanden, etc. Das in Fortran 90/95 als Datenbund/Struktur verwendbare type-Konstrukt wurde also in Fortran 2003 zu einer Klasse, ähnlich wie es in anderen Programmierspachen unter der Bezeichnung class bekannt ist, aufgerüstet.

Literatur

Bearbeiten

Weiterführende Literatur

Bearbeiten

<<< zur Fortran-Startseite
<< Fortran 95 Fortran 2003 >>
< Systemroutinen Offizielle Fortran 95-Erweiterungen >