Fortran
Programowanie obiektowe
Szukaj…
Definicja typu pochodnego
Fortran 2003 wprowadził obsługę programowania obiektowego. Ta funkcja pozwala skorzystać z nowoczesnych technik programowania. Typy pochodne są zdefiniowane w następującej formie:
TYPE [[, attr-list] :: ] name [(name-list)]
[def-stmts]
[PRIVATE statement or SEQUENCE statement]. . .
[component-definition]. . .
[procedure-part]
END TYPE [name]
gdzie,
- attr-list - lista specyfikatorów atrybutów
- name - nazwa typu danych pochodnych
- lista nazw - lista nazw parametrów typu oddzielonych przecinkami
- def-stmts - jedna lub więcej deklaracji INTEGER parametrów typu wymienionych na liście nazw
- definicja komponentu - jedna lub więcej instrukcji deklaracji typu lub instrukcji wskaźnika procedury określających komponent typu pochodnego
- procedura-część - instrukcja ZAWIERA, opcjonalnie po niej instrukcja PRYWATNA i jedna lub więcej instrukcji wiążących procedurę
Przykład:
type shape
integer :: color
end type shape
Procedury typu
W celu uzyskania zachowania podobnego do klasy, typ i powiązane procedury (podprogram i funkcje) należy umieścić w module:
Przykład:
module MShape
implicit none
private
type, public :: Shape
private
integer :: radius
contains
procedure :: set => shape_set_radius
procedure :: print => shape_print
end type Shape
contains
subroutine shape_set_radius(this, value)
class(Shape), intent(in out) :: self
integer, intent(in) :: value
self%radius = value
end subroutine shape_set_radius
subroutine shape_print(this)
class(Shape), intent(in) :: self
print *, 'Shape: r = ', self%radius
end subroutine shape_print
end module MShape
Później w kodzie możemy użyć tej klasy Shape w następujący sposób:
! declare a variable of type Shape
type(Shape) :: shape
! call the type-bound subroutine
call shape%set(10)
call shape%print
Abstrakcyjne typy pochodne
Rozszerzalny typ pochodny może być abstrakcyjny
type, abstract :: base_type
end type
Takiego typu pochodnego nie można nigdy utworzyć, na przykład przez
type(base_type) t1
allocate(type(base_type) :: t2)
ale obiekt polimorficzny może mieć to jako deklarowany typ
class(base_type), allocatable :: t1
lub
function f(t1)
class(base_type) t1
end function
Typy abstrakcyjne mogą mieć komponenty i procedury powiązane z typem
type, abstract :: base_type
integer i
contains
procedure func
procedure(func_iface), deferred :: def_func
end type
Procedura def_func
jest odroczoną procedurą związaną z func_iface
z interfejsem func_iface
. Taka odroczona procedura związana z typem musi zostać wdrożona przez każdy rozszerzający się typ.
Rozszerzenie typu
Typ pochodny jest rozszerzalny, jeśli nie ma ani atrybutu bind
ani atrybutu sequence
. Taki typ można rozszerzyć o inny typ.
module mod
type base_type
integer i
end type base_type
type, extends(base_type) :: higher_type
integer j
end type higher_type
end module mod
Zmienna polimorficzna o zadeklarowanym typie base_type
jest typu zgodnego z typem higher_type
i może mieć ten typ dynamiczny
class(base_type), allocatable :: obj
allocate(obj, source=higher_type(1,2))
Zgodność typów pochodzi z łańcucha dzieci, ale typ może obejmować tylko jeden inny typ.
Rozszerzony typ pochodny dziedziczy procedury powiązane z rodzicem, ale można to zmienić
module mod
type base_type
contains
procedure :: sub => sub_base
end type base_type
type, extends(base_type) :: higher_type
contains
procedure :: sub => sub_higher
end type higher_type
contains
subroutine sub_base(this)
class(base_type) this
end subroutine sub_base
subroutine sub_higher(this)
class(higher_type) this
end subroutine sub_higher
end module mod
program prog
use mod
class(base_type), allocatable :: obj
obj = base_type()
call obj%sub
obj = higher_type()
call obj%sub
end program
Konstruktor typów
Niestandardowe konstruktory można tworzyć dla typów pochodnych, używając interfejsu do przeciążenia nazwy typu. W ten sposób argumenty słów kluczowych, które nie odpowiadają komponentom, mogą być użyte podczas konstruowania obiektu tego typu.
module ball_mod
implicit none
! only export the derived type, and not any of the
! constructors themselves
private
public :: ball
type :: ball_t
real :: mass
end type ball_t
! Writing an interface overloading 'ball_t' allows us to
! overload the type constructor
interface ball_t
procedure :: new_ball
end interface ball_t
contains
type(ball_t) function new_ball(heavy)
logical, intent(in) :: heavy
if (heavy) then
new_ball%mass = 100
else
new_ball%mass = 1
end if
end function new_ball
end module ball_mod
program test
use ball_mod
implicit none
type(ball_t) :: football
type(ball_t) :: boulder
! sets football%mass to 4.5
football = ball_t(4.5)
! calls 'ball_mod::new_ball'
boulder = ball_t(heavy=.true.)
end program test
Może to być wykorzystane do stworzenia ciekawszego interfejsu API niż przy użyciu osobnych procedur inicjalizacji:
subroutine make_heavy_ball(ball)
type(ball_t), intent(inout) :: ball
ball%mass = 100
end subroutine make_heavy_ball
...
call make_heavy_ball(boulder)