Fortran面向对象
从Fortran 2003开始标准加强了面向对象(OOP)语法支持,通过使用模块(module)及复用派生类型关键字(type)定义Fortran的类。
Fortran主要参照C++ OOP模型进行设计,两者对比如下表所示。
C++ | Fortran | |
---|---|---|
成员 | 在类中定义 | 在'type'中定义 |
方法 | 在类中定义 | 在'type'中声明interface;在module中实现 |
构造函数 | default或explicit | 没有 |
对象本身 | this | 第一个参数 |
对象方法引用 | 点操作符'.’ | %操作符 |
Fortran类组织方式
- 在模块文件定义类(module ...type ...contains ... end type ...contains ... end module)
- type...contains...end type定义类(包括数据成员及类方法声明)
- 实际方法定义在模块contains部分实现
- 类方法的第一个参数是对象本身
module A
type B
! data members
integer :: c
contains
procedure :: d
end type
contains
subroutine d(v)
class(B) :: v
...
end subroutine
end module
Fortran 类定义
Fortran中type
关键字除了可用于定义派生类型,也可以用于定义类,通过在type
结构中增加contains
子句用于类方法的声明,而在module模块的contains
中实现类方法。
module mod_a
implicit none
!class declar as a type with contains clause
! followed by procedure declaration
!Fortran class based on type objects
type Scalar
!data member
real(4) :: value
contains
!methods declar in contains clause
procedure,public :: printme,scaled
end type Scalar
contains
!actual methods definition
subroutine printme(me)
implicit none
!object itself as extra parameter
!declar as class for polymorphism, allow extension
class(Scalar) :: me
print '("The value is", f7.3)',me%value
end subroutine printme
function scaled(me,factor)
implicit none
class(Scalar) :: me
real(4) :: scaled,factor
scaled = me%value * factor
end function scaled
end module mod_a
注意上面方法实现中第一个参数me
,这里使用class
关键字声明其类型,不能使用type声明其类型,因为printme和scaled是类方法(Fortran中称为type-bound procedure),class
支持多态对象哑元,即不用严格是该类,可以是继承类。
在类方法定义中传递的第一个参数是类对象本身,这很像C++中类方法中对象指针this
,在调用类方法时不需要显式传递调用对象本身(call obj%method(...))。
Fortran类成员方法调用
Fortran使用%
指定数据成员和方法,比如obj%value,obj%add()。而C++使用点操作符.
。
program main
use mod_a
implicit none
type(Scalar) :: x ! declare one object
real(4) :: y
!intialize object and data member should not be private
x = Scalar(-3.14)
!object itself as first implicit parameter like this pointer in C++
call x%printme()
y = x%scaled(2.)
print '(f7.3)',y
end program main
可以使用typename(value)
方式进行类对象的初始化。
使用类方法调用方式不需要显式传递对象本身参数。此外,类方法也可以当做普通的模块过程进行调用,此时实参必须哑元一一对应,对象参数需要显式传入,比如
call printme(x)
操作符重载
对于类对象如果想要像内置类型使用数学操作符(比如obj3 = obj1 + obj2)那样简洁操作,可以利用interface
功能定义操作符重载接口。
module PointClass
type,public :: Point
real(8) :: x,y
contains
procedure,public :: distance,add,printme
end type Point
interface operator(+) ! overriding operator +
module procedure add
end interface operator(+)
contains
real(8) function distance(this,that)
implicit none
class(Point) :: this
class(Point) :: that
distance = sqrt((that%x-this%x)**2 + (that%y-this%y)**2)
end function distance
! return type use type() not class type
type(Point) function add(this,that) result(total)
implicit none
! parameters need to be intent(in) for operator overloading
class(Point),intent(in) :: this
class(Point),intent(in) :: that
total%x = this%x + that%x
total%y = this%y + that%y
end function add
subroutine printme(this)
implicit none
class(Point) :: this
print*, "value is",this%x, this%y
end subroutine printme
end module PointClass
program test
use PointClass
implicit none
type(Point) :: p1,p2,sum
p1 = point(1.d0,1.d0)
p2 = point(4.d0,5.d0)
print*,"Distance:",p1%distance(p2)
! use method of class
sum = p1%add(p2)
call sum%printme()
! operator overloading
sum = p1 + p2
call sum%printme()
end program test
重载接口说明语法:
interface operator(+)
module procedure add !实际调用过程
end interface operator(+)
重载过程定义有限制:传入的对象哑元必须是intent(in)属性,这是可以理解的,比如加法操作不能改变操作数。
参考资料
- Eijkhout, Lindsey, Fortran classes and objects, 2021.
- Markus, Object-oriented programming - introduction, 2020.

