我正在尝试为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
。
答案 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).