2

我对 Fortran OOP 很陌生,并且在初始化父类型和派生类型时遇到了一些问题。我有一个包含父类型的模块object(对不起这个词的过度使用..)和它的派生类型circle,它有额外的radius字段。

我现在需要初始化object类型的方式需要对半径使用虚拟参数,我想避免这种方式。所以目前我有什么工作,但我想知道一种更好的方法来做到这一点,因为如果我object将来需要进一步派生类型,它似乎不太实用。

我想在这个意义上使用object作为抽象父类型会有所帮助吗?或者使用通用程序,但我真的不知道该怎么做。

代码如下。

module objectMod
  implicit none

  type :: object
     real,allocatable   :: x(:,:)           ! position vector (points) --- (M,{i,j})
     real               :: centre(2)        ! centre of the object
     integer            :: M=50             ! number of Lagrangian points of the object (default)
     real               :: eps=0.1          ! kernel of the surface (default)
   contains
     procedure :: init=>init_object
  end type object

contains

  subroutine init_object(a,centre,radius,M,eps)
    implicit none

    class(object),intent(inout)   :: a
    real,intent(in)               :: centre(2)
    integer,intent(in),optional   :: M
    real,intent(in),optional      :: eps
    real,intent(in),optional      :: radius ! ignored for object

    if(present(M)) a%M = M
    if(.not.allocated(a%x)) allocate(a%x(a%M,2))
    a%centre = centre
    if(present(eps)) a%eps = eps
  end subroutine init_object
end module objectMod

module geomMod
  use objectMod
  implicit none
  real,parameter :: PI = 3.14159265

  type,extends(object) :: circle
     real              :: radius    ! radius
   contains
     procedure :: init=>init_circle
  end type circle

contains

  subroutine init_circle(a,centre,radius,M,eps)
    implicit none
    class(circle),intent(inout) :: a
    real,intent(in)             :: centre(2)
    real,intent(in),optional    :: radius
    integer,intent(in),optional :: M
    real,intent(in),optional    :: eps

    integer :: i
    real    :: dtheta

    ! object type attributes initialization
    a%centre = centre
    if(present(M)) a%M = M
    if(.not.allocated(a%x)) allocate(a%x(a%M,2))
    if(present(eps)) a%eps = eps
    ! circle type attributes initialization
    a%radius = radius

    dtheta = 2.*PI/real(a%M-1)
    do i = 1,a%M
      a%x(i,1) = a%radius*cos(dtheta*(i-1))+a%centre(1)
      a%x(i,2) = a%radius*sin(dtheta*(i-1))+a%centre(2)
    end do
  end subroutine init_circle
end module geomMod
4

1 回答 1

1

您正在做的错误是将构造函数创建为类型绑定过程。参数列表必须符合的要求对您不利。

类型绑定过程根本不是构造函数(或初始化程序)的正确工具。

在 Fortran 中,我们使用返回对象实例的函数来初始化它。

function init_object(centre,M,eps) result(a)
  type(object) :: a
  real,intent(in)               :: centre(2)
  integer,intent(in),optional   :: M
  real,intent(in),optional      :: eps
  !NO radius
end function init_object


interface object
  procedure init_object
end interface



....



obj = object(my_centre, my_m, my_eps)

这也是调用默认结构构造函数的方式。


我看到您从一些在线教程中获取了示例。我发现这个例子是一个糟糕的设计。如果你不同意,你将不得不忍受它带来的这些问题。


因此,基本上,您想要一个初始化方法而不是构造函数(请参阅为什么使用初始化方法而不是构造函数?进行一些讨论。Objective-C 使用类似https://www.binpress.com/tutorial/objectivec-第 11 课-对象初始化/76但最近的 Swift 使用构造函数。)。

您可以通过这种方式使用泛型:

    module types

        type t1
          integer :: i
        contains
          generic :: init => init_t1
          procedure, private :: init_t1
        end type

        type, extends(t1) :: t2
          integer :: j
        contains
          generic :: init => init_t2
          procedure, private :: init_t2
        end type

        type, extends(t2) :: t3
          integer :: k
        contains
          generic :: init => init_t3
          procedure, private :: init_t3
        end type

    contains


      subroutine init_t1(self, i)
        class(t1) :: self
      end subroutine

      subroutine init_t2(self, i, j)
        class(t2) :: self
      end subroutine

      subroutine init_t3(self, i, j, k)
        class(t3) :: self
      end subroutine

    end module

在我看来,这很丑陋且非 Fortranic,但它可以满足您的需求。

您必须特别注意,用户不会因为会发出错误消息的东西重载而无意中调用了错误的版本。

我的建议仍然是遵循常见的模式,而不是发明自己的方式,这会使那些习惯于主流的人感到困惑。

于 2016-03-16T14:46:33.393 回答