多态父派生类型

Posted

技术标签:

【中文标题】多态父派生类型【英文标题】:Polymorphic parent derived type 【发布时间】:2016-07-03 19:54:31 【问题描述】:

我正在尝试设置一个 Fortran OOP 代码,其中父类型 geom 具有可分配字段 shape。该字段分配有geom 的扩展类型之一,它们是circlerectangle 类型。在另一个模块中,我有一个 body 类型,其中包含一个 geom 字段等。

所以基本上我想要一个 geom 类型,它实际上可以访问不同的类型(然后将根据类型访问不同的字段)和一个 body 类型,它使用几何初始化。

找到下面的代码。这是几何的模块:

module geomMod

  implicit none

  type :: geom
    class(*),allocatable  :: shape
  contains
    procedure,private     :: set_geom
    generic               :: assignment(=) => set_geom
  end type geom

  type,extends(geom) :: circle
    integer :: id=1
    real    :: centre(2)
    real    :: radius
  end type circle

  type,extends(geom) :: rectangle
    integer :: id=2
    real    :: centre(2)
    real    :: length(2)
  end type rectangle

contains

  subroutine set_geom(a,b)
    implicit none
    class(geom),intent(inout) :: a
    class(*),intent(in)    :: b

    allocate(a%shape,source=b)
  end subroutine set_geom

end module geomMod

这是正文的模块:

module bodyMod
  use geomMod
  implicit none

  type :: body
    class(geom),allocatable :: geom1
    real,allocatable        :: x(:,:)           
    integer                 :: M=50            
    real                    :: eps=0.1         
  contains
    procedure :: init
  end type body

contains

  subroutine init(a,geom1,M,eps)
    implicit none

    class(body),intent(inout)   :: a
    class(geom),intent(in)      :: geom1
    integer,intent(in),optional :: M
    real,intent(in),optional    :: eps

    allocate(a%geom1,source=geom1)

    if(present(M)) a%M = M
    if(present(eps)) a%eps = eps
    if(.not.allocated(a%x)) allocate(a%x(a%M,2))
  end subroutine init

end module bodyMod

这就是我从主文件初始化它们的方式:

  use bodyMod
  implicit none

  integer,parameter :: M = 500
  real,parameter    :: eps = 5

  type(body) :: b
  type(geom) :: geom1

  geom1 = circle(centre=(/1,1/),radius=0.5)

  call b%init(geom1=geom1,M=M,eps=eps)

但是,使用 gfortran 4.8.4 编译时出现此错误。

  geom1 = circle(centre=(/1,1/),radius=0.5)
          1
Error: No initializer for component 'shape' given in the structure constructor at (1)!

【问题讨论】:

gfortran 4.8.4。有了那个标签,我的意思是我使用 F2003 OOP 样式而不是 F90 进行编码。但我已将其删除,因为它可能会造成混淆。 我已经根据解决错误消息进行了回答,但我不完全确定我是否遵循您的预期设计。拥有一个扩展类型有一个继承的组件,你打算用扩展类型本身的动态类型分配它让我感到困惑。不过,我没有密切关注你之前关于这个主题的问题,所以我可能遗漏了一些东西。这就是说我打算回答有关错误的问题,而不是评论设计方面。 把“geom”改成一个空的基类,简单地做为“call b% init(circle( center=[1.0,1.0], radius=0.5)),不是更简单吗? M=M, eps=eps )" ? (因为“body”已经有了“class(geom), allocatable”,这似乎足以容纳几何信息。) 是的。我也想过将geom 作为一个空类。但我希望geom 持有circle,然后使用geom 初始化body 是的,抱歉,应该检查两次。 【参考方案1】:

您的结构构造函数 circle 仅使用指定的两个组件的值进行引用

geom1 = circle(centre=(/1,1/),radius=0.5)

而你的编译器不喜欢这样。

circle 类型有四个组件,shapeidcentreradius。在对结构构造函数的引用中,并不总是需要为所有组件提供值,这里有两种情况适用。

第一种情况是组件具有默认初始化。正如您所做的那样,您完全有权省略组件 id 的值。

第二种情况是针对可分配组件的。这就是问题所在:Fortran 2003 和 Fortran 2008 标准之间的规则发生了变化。

在 Fortran 2008 下,允许省略对应于可分配组件的值。在 Fortran 2003 中不是。

您的编译器错误表明它在这方面遵循 Fortran 2003 的规则,并且它需要可分配组件 shape 的值。 gfortran 是such a compiler。

要提供这样一个值,让组件不分配,可以有

geom1 = circle(shape=NULL(),centre=(/1,1/),radius=0.5)

【讨论】:

您好,谢谢!我不知道,这很有帮助。我还看到我实际上可以将shape 声明为指针class(*),pointer :: shape=>null(),这样我就可以对其进行初始化。但是,我试图访问radius,但我无法做到:print*,b%geom1%radius 我知道radius 不是geom 结构的成员。这不是很明显,但我希望我可以通过这种方式访问​​,因为我在allocate(a%shape,source=b) 中分配了shape 现在指向包含radiuscircle 类型。我怎么能那样做?再次感谢。 使用指针组件,确实可以将指针的默认状态设置为未关联。但是,为了获得这一优势,我会谨慎地选择指针组件而不是可分配组件。 那我怎么才能访问radius呢? 对于您的其他评论部分,我没有看到对set_geom 的调用,也没有通过定义的分配调用。您是否认为在您给出的示例中有这样的调用? [如果需要更详细的答案,可能更适合提出一个新问题(在通常的研究之后等),并在那里提供更多细节。] 我确实错过了geom1 不是circle 类型,因此有定义的分配...b%geom1%shape%radius 是您想要的参考-但您不能为@ 这样做声明类型为geom 的987654347@(没有组件radius)。您需要做进一步的工作(例如select type)才能获得所需的参考。 [搜索关于声明类型和动态类型之间区别的问答。]

以上是关于多态父派生类型的主要内容,如果未能解决你的问题,请参考以下文章

继承派生新式类与经典类

C++ 静态多态性 (CRTP) 和使用派生类的 typedef

初识继承和多态

初始继承和多态

类的继承派生组合菱形的继承多态

面向对象进阶1