【问题标题】:Understanding Fortran extends types and override理解 Fortran 扩展类型和覆盖
【发布时间】:2014-10-14 03:37:15
【问题描述】:

我正在尝试理解 Fortran 2003 标准(或更高版本)中的面向对象概念。我对 C++ 有一些了解,所以我认为这两种语言之间有一些共同的想法可以帮助我更好地理解它们。

在 C++ 中,多态性是通过类派生和成员函数覆盖来完成的。一个定义了一个“抽象”基类,其中几乎所有的虚函数都被定义了。不同的派生类包含它们的实际实现。所以其他功能只需要基于“抽象”类进行编程即可。然后它们适用于所有派生类。

我认为在 Fortran 中,OOP 以类似的方式完成,但存在一些差异。在我看来,需要像 C++ 一样定义一个带有一些虚函数的基类型。其他函数/子例程应遵循基类型中的成员函数定义。这就是解决所有扩展类型的函数/子例程重用的方法。

我对如何编程这个想法没有更好的想法。这是我的第一次尝试:

type Basis
    integer                                 :: NBasis
contains
    private
    procedure                               :: DoNothing
    generic, public                         :: Constructor => DoNothing
    generic, public                         :: AllocateBasis => DoNothing
endtype Basis

type, extends(Basis) :: GridBasis
    private
    integer                                 :: NGrid
contains
    private
    procedure                               :: ConstructorGrid1
    procedure                               :: ConstructorGrid2
    generic, public                         :: Constructor => ConstructorGrid1, ConstructorGrid2, ConstructorGrid3
    procedure                               :: AllocateGridReal
    procedure                               :: AllocateGridCplx
    generic, public                         :: AllocateBasis => AllocateGridReal, AllocateGridCplx
endtype GridBasis
  • 首先,如何在类型 Basis 中定义“AllocateBasis”,使其像“虚拟函数”一样工作,并且所有扩展类型都必须定义自己的“AllocateBasis”版本?

  • 其次,如何在GridBasis类型中定义“AllocateBasis”?这里的定义包含了它的真正实现。

  • 第三,如何让GridBasis类型的“AllocateBasis”成为重载函数?即有真实版本和复杂版本,它们都被命名为“AllocateBasis”,带有真实或复杂的输入可分配数组。

  • 第四,NOPASS 与 PASS。据我了解,如果设置了 PASS,则有一个指向该对象的显式指针。但是当设置了 NOPASS 时,就没有这样的事情了。所以PASS是为了澄清而简化?

【问题讨论】:

  • 我的目标是将 Solver 例程和基础类型分开。你说的对。我会立即在我的帖子中添加一个示例。感谢您的回复:)
  • 您现在有了一个合理的通用答案,可以轻松适应您的具体情况。而且您的编辑澄清了您知道您不需要在任何地方都做基本的事情(但在这里很有用)。

标签: inheritance polymorphism fortran overriding


【解决方案1】:

首先是一些cmets/对您的问题的回答:

  • 您可以将类型绑定过程声明为deferred。然后您必须仅在没有特定实现的情况下定义其签名(接口)。包含deferred 过程的类型必须声明为abstract。这样的类型不能被实例化。所有扩展类型都必须提供给定过程的实现,除非它们本身是abstract

  • 为了在扩展类型中为deferred 过程提供实现,您只需在扩展类型中声明该过程并为其提供实现。

  • 您不能将给定类型的公共过程转换为扩展类型中的generic。但是,您可以在基本类型中定义 generic,并在其派生类型中扩展它。

  • pass 属性是默认设置的,因此过程的 first 参数将是类型实例。但是,您可以指定它以使其更明确。此外,您可以以 PASS(ARGNAME) 的形式使用它来指定哪个参数 (ARGNAME) 应该是实例。此参数不必是过程中的第一个参数。

您可以在下面找到一个自包含示例,其中应包含您要求的所有功能:

module basis_module
  implicit none

  type, abstract :: Basis
    integer :: NBasis
  contains
    procedure(allocBasisR1Interface), deferred :: allocateBasisR1
    generic :: allocateBasis => allocateBasisR1
  end type Basis

  interface 
    ! Interface for real basis allocation
    subroutine allocBasisR1Interface(self, array)
      import
      class(Basis), intent(inout) :: self
      real, intent(in) :: array(:)
    end subroutine allocBasisR1Interface
  end interface

end module basis_module


module extension_module
  use basis_module
  implicit none

  type, extends(Basis) :: GridBasis
  contains
    ! Extending the mapping allocateBasis => allocateBasisR1 of
    ! the parent type.
    generic :: allocateBasis => allocateBasisC1
    procedure :: allocateBasisC1
    ! Implementation for the deferred procedure in Basis
    procedure :: allocateBasisR1
  end type GridBasis

contains

  subroutine allocateBasisR1(self, array)
    class(GridBasis), intent(inout) :: self
    real, intent(in) :: array(:)

    self%NBasis = size(array)
    print *, "GridBasis:allocateBasisR1"

  end subroutine allocateBasisR1


  subroutine allocateBasisC1(self, array)
    class(GridBasis), intent(inout) :: self
    complex, intent(in) :: array(:)

    self%NBasis = size(array)
    print *, "GridBasis:allocateBasisC1"

  end subroutine allocateBasisC1

end module extension_module


program test
  use extension_module
  implicit none

  type(GridBasis) :: mybasis
  real :: myRealArray(10)
  complex :: myComplexArray(5)

  call mybasis%allocateBasis(myRealArray)
  call mybasis%allocateBasis(myComplexArray)

end program test

【讨论】:

  • 很好的答案!谢谢!让我消化一会儿。只是好奇:您是否有理由将不同模块中的基本类型和扩展类型分开,或者只是您的编程风格?
  • 这只是我的编程风格,为每种类型与类型绑定过程使用不同的模块。
  • @Bálint-Aradi,我知道这是一个过时的线程,但这段代码是否曾经运行过?它在 IVF 2012 下生成与符号“_allocBasisR1Interface”相关的 LNK2019 错误,我缺乏修复它的必要知识。
  • @IronX 是的。我刚刚尝试过 gfortran 8.2。
猜你喜欢
  • 2021-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
相关资源
最近更新 更多