派生类型和扩展类型构造

时间:2016-03-16 12:48:23

标签: oop fortran abstract extend fortran2003

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

我需要初始化object类型的方法现在需要为radius使用伪参数,我想避免。所以目前我的工作方式,但我想知道一个更好的方法来做到这一点,因为如果我将来需要从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

1 个答案:

答案 0 :(得分:0)

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

类型绑定过程根本不适合构造函数(或初始化程序)。

在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)

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

我看到你从一些在线教程中取得了榜样。我觉得这个例子很糟糕。如果你不同意,你将不得不忍受它带来的这些问题。

所以,基本上,你需要一个初始化方法而不是构造函数(参见Why use an initialization method instead of a constructor?进行一些讨论。目标-C使用类似https://www.binpress.com/tutorial/objectivec-lesson-11-object-initialization/76的东西,但更新的Swift使用构造函数。)。< / p>

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

    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,但它做你想要的。

您必须特别注意用户不会因为发出错误消息的内容而过度调用错误的版本。

我的建议仍然是遵循常见的模式,而不是发明自己的方式,这会混淆那些习惯主流的人。