派生类型的构造方法

时间:2019-11-02 06:11:15

标签: constructor fortran derived-types

我正在尝试为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

2 个答案:

答案 0 :(得分:4)

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不接受无参数引用。而是使用默认的结构构造函数。

您必须以与cat接口相匹配的方式引用通用init_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的分配。

答案 1 :(得分: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).