首页 > 解决方案 > 在 Fortran 中分配和构造多态对象的规范方法是什么?

问题描述

我想创建一个多态对象数组,这些对象的构造函数根据它们的动态类型采用不同的虚拟参数。在阅读了用户定义和结构构造函数之后,我发现无法将这些概念应用于动态分配的对象。有 C++ 的背景,我习惯于在动态分配对象或在堆栈上分配对象时可以使用同一个构造函数“成员函数”,但是如何在分配的对象上显式调用用户定义的 Fortran 构造函数?

相反,我试图摆弄通用和类型绑定的 init 函数:

module mod
type :: basis_t
contains
    procedure, public :: init_func => init_base
    ! I want a generic constructor function
    generic, public   :: init => init_func 
end type

type, extends(basis_t) :: extended_t
contains
    ! cannot work, init_extended has a different signature from init_base
    procedure, public :: init => init_extended 
end type

type wrapper_t
   type(basis_t), pointer :: obj
end type

contains
   subroutine init_base(this)
      class(base_t), intent(inout) :: this
   end subroutine

   subroutine init_extended(this, param)
      class(extended_t), intent(inout) :: this
      integer :: param
   end subroutine
end module

program
   use mod
   implicit none

   type(wrapper_t) :: arr(2)
   allocate(basis_t::arr(1)%obj)
   allocate(extended_t::arr(2)%obj)
   call arr(1)%obj%init    ! calls init_basis
   call arr(2)%obj%init(4) ! calls init_extended
end program

但我不相信我走在正确的轨道上,例如在 C++ 中,我宁愿这样做

basis_t* arr[2];
arr[0] = new basis_t;
arr[1] = new extended_t{ 4 };

重要的区别是 C++ 中的构造函数不是类型绑定/虚拟的,就像我的 Fortran 方法一样。我能做些什么?

标签: oopconstructorfortranpolymorphism

解决方案


Fortran 中构造函数的作用可以通过以下方式提供:

  • 该语言提供了结构构造函数

  • 一个函数,其结果是正在构造的对象的类型。该语言允许泛型函数与派生类型具有相同的名称,并进一步允许对此类函数的引用重载该类型的结构构造函数的引用。

  • 定义适当类型的意图(输出)参数的子例程。

你使用什么在一定程度上取决于环境和个人喜好。语言提供的结构构造函数在某些情况下可以用在常量表达式中,但只允许对组件进行简单的值定义(没有可执行代码);函数引用形式允许您执行任意代码作为对象构造的一部分,不能在常量表达式中使用,不能轻易指示构造失败并且如果构造的对象很大,可能会很昂贵(取决于 Fortran 处理器实现细节);子例程形式需要单独的调用语句(构造函数不能是更大表达式的一部分)并且不能利用通用名称/结构重载语言功能。

这三种方法都不涉及类型绑定过程。在某些情况下,类型绑定过程可能适用于对象定义(例如,旨在从文件中读取对象值的类型绑定过程 - 扩展层次结构中的所有类型都需要有关要传递给的文件的相同信息它们),但它对构造没有一般意义,您在其中定义对象的类型以及定义其值。

Fortran 中的指针主要用于引用语义(因为它们是引用)。如果你想要值语义,你通常不想使用它们——使用可分配的。

TYPE :: ta
  INTEGER :: a
END TYPE ta

TYPE, EXTENDS(ta) :: tb
  REAL :: b
END TYPE :: tb

INTERFACE tb
  PROCEDURE :: tb_construct
END INTERFACE tb

TYPE, EXTENDS(ta) :: tc
END TYPE tc

TYPE :: ta_item
  CLASS(ta), ALLOCATABLE :: item
END TYPE ta_item

!...

FUNCTION tb_construct(arg)
  INTEGER, INTENT(IN) :: arg
  TYPE(tb) :: tb_construct
  tb_construct%a = arg + 1
  tb_construct%b = arg / 2.0
END FUNCTION tb_construct

SUBROUTINE ConstructTC(obj, arg, stat)
  CLASS(ta), INTENT(OUT), ALLOCATABLE :: obj
  INTEGER, INTENT(IN) :: arg
  INTEGER, INTENT(OUT) :: stat
  TYPE(tc), ALLOCATABLE :: tmp
  IF (arg < 0) THEN
    ! Construction failed.
    stat = 1
    RETURN
  END IF
  tmp%a = arg + 4
  CALL MOVE_ALLOC(tmp, obj)
  stat = 0    ! Construction succeeded.
END SUBROUTINE ConstructTC

!...

TYPE(ta_item) :: the_items(3)
INTEGER :: stat

! Structure constructor
the_items(1)%item = ta(1)

! Overloaded function.
the_items(2)%item = tb(2)

! Subroutine.
CALL ConstructTC(the_items(3)%item, 3, stat)
IF (stat /= 0) ERROR STOP 'It failed.'

推荐阅读