多态父派生类型
Posted
技术标签:
【中文标题】多态父派生类型【英文标题】:Polymorphic parent derived type 【发布时间】:2016-07-03 19:54:31 【问题描述】:我正在尝试设置一个 Fortran OOP 代码,其中父类型 geom
具有可分配字段 shape
。该字段分配有geom
的扩展类型之一,它们是circle
或rectangle
类型。在另一个模块中,我有一个 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
类型有四个组件,shape
、id
、centre
和 radius
。在对结构构造函数的引用中,并不总是需要为所有组件提供值,这里有两种情况适用。
第一种情况是组件具有默认初始化。正如您所做的那样,您完全有权省略组件 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
现在指向包含radius
的circle
类型。我怎么能那样做?再次感谢。
使用指针组件,确实可以将指针的默认状态设置为未关联。但是,为了获得这一优势,我会谨慎地选择指针组件而不是可分配组件。
那我怎么才能访问radius
呢?
对于您的其他评论部分,我没有看到对set_geom
的调用,也没有通过定义的分配调用。您是否认为在您给出的示例中有这样的调用? [如果需要更详细的答案,可能更适合提出一个新问题(在通常的研究之后等),并在那里提供更多细节。]
我确实错过了geom1
不是circle
类型,因此有定义的分配...b%geom1%shape%radius
是您想要的参考-但您不能为@ 这样做声明类型为geom
的987654347@(没有组件radius
)。您需要做进一步的工作(例如select type
)才能获得所需的参考。 [搜索关于声明类型和动态类型之间区别的问答。]以上是关于多态父派生类型的主要内容,如果未能解决你的问题,请参考以下文章