跳转到内容

Fortran/OOP 在 Fortran 中

来自 Wikibooks,开放世界的开放书籍

面向对象编程

[编辑 | 编辑源代码]

数据可以收集在 module 中。一般形式如下所示

module <name>
    [use <module_names>]
    [<declarations>]
contains
    [<subroutines and functions>]
end module [<name>]

数据访问

[编辑 | 编辑源代码]

有三种可能的访问属性:public, private, protected

  • public:外部代码具有读写访问权限。
  • private:外部代码无访问权限。
  • public, protected:外部代码具有读访问权限。

在其他代码中使用模块

[编辑 | 编辑源代码]

可以在外部代码中包含模块的公共数据。有三种方法。

  • use <moduleName>:包含所有公共数据和方法
  • use <moduleName>, <renames>:包含所有公共数据和方法,但会重命名一些公共数据或方法
  • use <moduleName>, only: <subset>:仅包含一些公共数据和方法
总体概述
[编辑 | 编辑源代码]
module test_m
    implicit none
    private  ! All data is by default private.
    ! These procedures are set public -> accessible outside
    public print_coords, set_coords 
    real :: x, y  ! Not accessible outside module.
contains
    subroutine print_coords
        print *, "x, y", x, y
    end subroutine

    subroutine set_coords(new_x, new_y)
        real, intent(in) :: new_x, new_y

        x = new_x
        y = new_y
    end subroutine
end module

program main
    use test_m  ! Import the "test_m" module
    implicit none

    call set_coords(1.0, 1.0)  ! Call the public procedure from test_mod
    call print_coords
end program
数据访问
[编辑 | 编辑源代码]
module data_access_m
    implicit none
    private
    public a, b
    protected b
    private c
    integer :: a = 1
    integer :: b = 1
    integer :: c = 1
end module

program main
    use data_access_m

    ! Accessing public object works.
    print *, a
    ! Editing public object works.
    a = 2
    ! Accessing protected object works.
    print *, b
    ! Editing protected object does not work.
    !b = 2 <- ERROR
    ! Accessing private object does not work
    !print *, c <- ERROR
    ! Editing private object does not work
    !c = 2 <- ERROR
end program
使用模块
[编辑 | 编辑源代码]
module test_module
    implicit none
    private
    integer, public :: a = 1
    integer, public, protected :: b = 1
    integer, private :: c = 1
end module test_module

!> Import all public data of test_module.
program main
    use test_module

    print *, a, b
end program main

!> Import all data, and rename.
program main
    use test_module, better_name => a

    ! New name use available.
    print *, better_name
    ! Old name is not available anymore.
    !print *, a  <- ERROR
end program main

!> Import only a subset of the public data.
program main
    use test_module, only : a

    ! Only a is loaded
    print *, a
    ! b is not loaded
    !print *, b  <- ERROR
end program main

子模块

[编辑 | 编辑源代码]

可以使用子模块扩展模块。出现了多种优势

  • 拆分大型模块
  • 拆分接口定义和实现,以便如果实现发生更改,则不需要重新编译依赖模块
  • 两个模块需要彼此的数据。
定义和实现的拆分
[编辑 | 编辑源代码]
!> simple module about circles
module circle_mod
    implicit none
    private
    public :: area, radius
    real            :: radius
    real, parameter :: PI = 3.1415

    interface  ! Interface block needed. Each function implemented via submodule needs an entry here.
        module function area() ! Important. Note the "module" keyword.
            real :: area
        end function 
    end interface
end module 

submodule (circle_mod) circle_subm  ! Submodule (parent_mod) child_mod.
contains
    module function area()  ! Again "module" keyword.
        area = PI*radius**2
    end function 
end submodule

program main
    use circle_mod
    implicit none

    radius = 1.0
    print *, "area:", area()
end program

派生数据类型

[编辑 | 编辑源代码]

在 Fortran 中,可以从其他结构中派生结构,称为派生数据类型。派生类型将具有父类型的功能以及新添加的功能,并且通用语法如下所示

type, extends(<parentTypeName>) :: <newTypeName>
    <definitions>
end type

以下示例显示了公司中不同类型的人员。

module company_data_mod
    implicit none
    private
    public phone_type, address_type, person_type, employee_type, salaried_worker_type, hourly_worker_type

    type phone_type
        integer :: area_code, number
    end type

    type address_type
        integer                        :: number
        character (len=:), allocatable :: street, city
        character (len=2)              :: state
        integer                        :: zip_code
    end type

    type person_type
        character (len=:), allocatable :: name
        type (address_type)            :: address
        type (phone_type)              :: phone
        character (len=:), allocatable :: remarks
    end type

    type, extends (person_type) :: employee_type
        integer :: phone_extension, mail_stop, id_number
    end type

    type, extends (employee_type) :: salaried_worker_type
        real :: weekly_salary
    end type

    type, extends (employee_type) :: hourly_worker_type
        real :: hourly_wage, overtime_factor, hours_worked
    end type
end module

program main
    use company_data_mod
    implicit none

    type (hourly_worker_type) :: obj
end program

析构函数

[编辑 | 编辑源代码]

可以定义在对象自动删除(超出范围)之前调用的过程。这是使用语句 final 完成的。以下示例说明了这一点

module person_m
    implicit none

    type person
        integer, allocatable :: numbers(:)
    contains
        final :: del
    end type
contains
    subroutine del(this)
        !! example for a derived type's destructor. allocatables are
        !! deallocated automatically anyways. this is just to show the usage of
        !! "final".
        type (person), intent (inout) :: this

        if (allocated(this%numbers)) deallocate (this%numbers)
    end subroutine 
end module

抽象基类型和延迟过程

[编辑 | 编辑源代码]

可以将基类型设置为 abstract,以便无法初始化该类型的对象,但可以从该类型派生子类型(通过 extends)。应该在子类型中定义的特定过程需要属性 deferred 以及显式接口。

以下示例说明了它们的使用。

module shape_m
    implicit none

    type, abstract :: shape
        real :: a, b
    contains
        procedure :: print => shape_print
        procedure (area_shape), deferred :: area
    end type

    interface
        real function area_shape(this)
            import :: shape
            class (shape), intent (in) :: this
        end function
    end interface
contains
    subroutine shape_print(this)
        class (shape), intent (in) :: this

        print *, 'a,b', this%a, this%b
    end subroutine
end module

module line_m
    use shape_m
    implicit none
    private
    public line

    type, extends (shape) :: line
    contains
        procedure :: area
    end type
contains
    real function area(this)
        class (line), intent (in) :: this

        area = abs(this%a - this%b)
    end function
end module

module rectangle_m
    use shape_m
    implicit none
    private
    public rectangle

    type, extends(shape) :: rectangle
    contains
        procedure :: area
    end type
contains
    real function area(this)
        class (rectangle), intent (in) :: this

        area = this%a * this%b
    end function
end module

program main
    use line_m
    use rectangle_m
    implicit none

    type (line)      :: l
    type (rectangle) :: r

    ! line
    l%a = 2.0
    l%b = 4.0
    print *, "line ...  "
    call l%print
    print *, "-> from:  ", l%a
    print *, "-> to:    ", l%b
    print *, "-> length:", l%area()

    ! rectangle
    r%a = 3.0
    r%b = 5.0
    print *
    print *, "rectangle ..."
    call r%print
    print *, "-> side a:", r%a
    print *, "-> side b:", r%b
    print *, "-> area:  ", r%area()
end program

多态指针

[编辑 | 编辑源代码]

可以使用 allocate 语句和 select type 环境中的类型定义来创建指向子类的指针。以下示例突出显示了它的使用。

module shape_m
    implicit none

    type, abstract :: shape
        ! Just an empty class used to implement a parent class.
        ! reason for abstract: there shouldnt be objects of TYPE(!) shape, just 
        ! polymorphic CLASS instances.
    end type
end module

module line_m
    use shape_m
    implicit none

    type, extends (shape) :: line
        ! A child class w/ one attribute.
        ! Reason for extends(shape): polymorphic shape pointer can point to
        ! objects of this type.
        real :: length
    end type
end module

module rectangle_m
    use shape_m
    implicit none

    type, extends (shape) :: rectangle
        ! A child class w/ another attribute
        ! Reason for extends(shape): (see explanation in line type)
        real :: area
    end type
end module

program main
    use rectangle_m
    use line_m
    implicit none

    class (shape), allocatable :: sh  ! Pointer to parent class.
    ! allocate (line :: sh)
    allocate (rectangle :: sh)  ! Allocate using child types
    select type (x => sh)   ! Associate block. "x" will be a pointer to the child object and of its type(!!)
        type is (line)      ! Select the right child type (the one we used in the allocate statement)
            x%length = 1.0
            print *, 'line length', x%length
        type is (rectangle)
            x%area = 2.0
            print *, 'rectangle area', x%area
        ! class is ()  ! Select by using classes.
        class default  ! If nothing of the above applied.
            error stop 'class/type not specified!'
    end select
end program
华夏公益教科书