【问题标题】:Constructor of derived types派生类型的构造函数
【发布时间】:2020-02-28 07:26:08
【问题描述】:

我正在尝试为solve this other question 的抽象类型的派生 类型编写一个构造函数,但它似乎不起作用,或者更好的是,它根本没有被调用。

目的是让运行时多态性设置正确的动物腿数。

这是两个模块:

动物

module animal_module
    implicit none

    type, abstract :: animal
        private
        integer, public :: nlegs = -1
    contains
        procedure :: legs
    end type animal

contains

    function legs(this) result(n)
        class(animal), intent(in) :: this
        integer :: n

        n = this%nlegs
    end function legs

module cat_module
    use animal_module, only : animal
    implicit none

    type, extends(animal) :: cat
        private
    contains
        procedure :: setlegs => setlegs
    end type cat

    interface cat
        module procedure init_cat
    end interface cat

contains

    type(cat) function init_cat(this)
        class(cat), intent(inout) :: this
        print *, "Cat!"
        this%nlegs = -4
    end function init_cat

主程序

program oo
    use animal_module
    use cat_module
    implicit none

    type(cat) :: c
    type(bee) :: b

    character(len = 3) :: what = "cat"

    class(animal), allocatable :: q

    select case(what)
    case("cat")
        print *, "you will see a cat"
        allocate(cat :: q)
        q = cat() ! <----- this line does not change anything

    case default
        print *, "ohnoes, nothing is prepared!"
        stop 1
    end select

    print *, "this animal has ", q%legs(), " legs."
    print *, "cat  animal has ", c%legs(), " legs."
end program

构造函数根本没有被调用,腿数仍然保持在-1

【问题讨论】:

    标签: constructor fortran derived-types


    【解决方案1】:

    cat 类型的可用非默认构造函数由模块过程init_cat 给出。你定义的这个函数像

    type(cat) function init_cat(this)
        class(cat), intent(inout) :: this
    end function init_cat
    

    这是一个有一个参数的函数,class(cat)。在您以后的参考中

    q = cat()
    

    通用cat 下没有与该引用匹配的特定函数:函数init_cat 不接受无参数引用。而是使用默认结构构造函数。

    您必须以与您的init_cat 接口匹配的方式引用通用cat 才能调用该特定函数。

    你想改变你的 init_cat 函数看起来像

    type(cat) function init_cat()
        ! print*, "Making a cat"
        init_cat%nlegs = -4
    end function init_cat
    

    然后你可以根据需要引用q=cat()

    请注意,在原始文件中,您尝试“构造”cat 实例,但您并未将构造的实体作为函数结果返回。相反,您正在修改一个参数(已经构造)。结构构造函数旨在用于返回这些有用的东西。

    另请注意,您不需要

    allocate (cat :: q)
    q = cat()
    

    q 的内在分配已经处理了q 的分配。

    【讨论】:

      【解决方案2】:

      FWIW,这里是一些比较三种方法的示例代码(方法 = 1:源分配,2:多态分配,3:混合方法)。

      module animal_module
          implicit none
      
          type, abstract :: animal_t
              integer :: nlegs = -1
          contains
              procedure :: legs   !! defines a binding to some procedure
          endtype
      
      contains
          function legs(this) result(n)
              class(animal_t), intent(in) :: this
                  !! The passed variable needs to be declared as "class"
                  !! to use this routine as a type-bound procedure (TBP).
              integer :: n
              n = this % nlegs
          end
      end
      
      module cat_module
          use animal_module, only : animal_t
          implicit none
      
          type, extends(animal_t) :: cat_t
          endtype
      
          interface cat_t   !! overloads the definition of cat_t() (as a procedure)
              module procedure make_cat
          end interface
      
      contains
          function make_cat() result( ret )   !! a usual function
              type(cat_t) :: ret   !<-- returns a concrete-type object
              ret % nlegs = -4
          end
      end
      
      program main
          use cat_module, only: cat_t, animal_t
          implicit none
          integer :: method
      
          type(cat_t) :: c
          class(animal_t), allocatable :: q
      
          print *, "How to create a cat? [method = 1,2,3]"
          read *, method
      
          select case ( method )
              case ( 1 )
                  print *, "1: sourced allocation"
      
                  allocate( q, source = cat_t() )
      
                  !! An object created by a function "cat_t()" is used to
                  !! allocate "q" with the type and value taken from source=.
                  !! (Empirically most stable for different compilers/versions.)
      
              case ( 2 )
                  print *, "2: polymorphic assignment"
      
                  q = cat_t()
      
                  !! Similar to sourced allocation. "q" is automatically allocated.
                  !! (Note: Old compilers may have bugs, so tests are recommended...)
      
              case ( 3 )
                  print *, "3: mixed approach"
      
                  allocate( cat_t :: q )
                  q = cat_t()
      
                  !! First allocate "q" with a concrete type "cat_t"
                  !! and then assign a value obtained from cat_t().
      
              case default ; stop "unknown method"
          endselect
      
          c = cat_t()
          !! "c" is just a concrete-type variable (not "allocatable")
          !! and assigned with a value obtained from cat_t().
      
          print *, "c % legs() = ", c % legs()
          print *, "q % legs() = ", q % legs()
      end
      
      --------------------------------------------------
      Test
      
      $ gfortran test.f90   # using version 8 or 9
      
      $ echo 1 | ./a.out
       How to create a cat? [method = 1,2,3]
       1: sourced allocation
       c % legs() =           -4
       q % legs() =           -4
      
      $ echo 2 | ./a.out
       How to create a cat? [method = 1,2,3]
       2: polymorphic assignment
       c % legs() =           -4
       q % legs() =           -4
      
      $ echo 3 | ./a.out
       How to create a cat? [method = 1,2,3]
       3: mixed approach
       c % legs() =           -4
       q % legs() =           -4
      
      --------------------------------------------------
      Side notes
      
      * It is also OK to directly use make_cat() to generate a value of cat_t:
        e.g., allocate( q, source = make_cat() ) or q = make_cat().
        In this case, we do not need to overload cat_t() via interface.
      
      * Another approach is to write an "initializer" as a type-bound procedure,
        and call it explicitly as q % init() (after allocating it via
        allocate( cat_t :: q )). If the type contains pointer components,
        this approach may be more straightforward by avoiding copy of
        components (which can be problematic for pointer components).
      

      【讨论】:

      • 通过这个比较,可能值得注意的是,可终结类型存在细微差别。
      • @francescalus 我希望我可以添加一些关于“可终结类型”的注释,但到目前为止我从未使用过“final”,所以无法解释......(相反,我通常称之为“fin ()”例程手动)。我会玩它来获得经验。
      猜你喜欢
      • 2023-03-10
      • 1970-01-01
      • 2016-07-19
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      • 2018-07-21
      • 2018-07-16
      • 2012-11-06
      相关资源
      最近更新 更多