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)


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow