现代Fortran包含各种面向对象的思想,包括通过FINAL
关键字的“析构函数”的概念。
MODULE mobject
TYPE :: tobject
! Data declarations
CONTAINS
FINAL :: finalize
END TYPE
CONTAINS
SUBROUTINE finalize(object)
TYPE(tobject) object
...
END SUBROUTINE
END MODULE
但是,此功能可靠吗?值得注意的是,我注意到有关何时以及是否调用它的不一致之处,并且在Intel Fortran 19和GFortan 7、8之间存在重大差异:
我注意到gfortran-7.4.0和gfortran-8.2.1.2之间没有区别。
这些不一致之处引发了一些关于析构函数对我的实际可用性的问题。两种行为是否完全符合标准?标准不清楚吗?该标准是否可能包含导致不直观行为的条款?
PROGRAM Block。对于IPRO将在PROGRAM块中声明的实例,Gfortran将不会调用析构函数(请参见示例中的run1
。
Scalar对象。对于声明为标量的实例,如果变量已进行任何形式的初始化,则Gfortran和IFort都将调用析构函数。但是,Intel Fortran在分配函数返回值时也将调用它
newObject
函数的末尾。但是,可以通过明确检查是否对象进行初始化,然后执行任何清理。
这意味着,程序员必须明确检查实例是否已初始化。
数组中的对象。如果对象包含在数组中,并且数组超出范围,则>
allocatable
,都没有区别。通过分配初始化的可分配数组。
来自函数的可分配/指针。
allocatable
对象或pointer
返回给对象,而是在将值显式地或通过释放在客户端代码中释放时调用它allocatable
s的范围。这就是我所期望的。allocatable
时,而不是声明为pointer
时,则不是,Intel Fortran在退出函数时,将对函数的本地值调用析构函数。var = newObject(...)
)初始化函数内部的对象时,或者在pointer
变体的情况下使用显式分配(allocate(var); var = newObject(...)
)初始化对象时,在未初始化的内存上调用析构函数,该内存在run5MoveAlloc
中可见]和run6MovePtr
中的%name
包含垃圾数据。可以通过使用allocate(var); call var%init(...)
模式来解决。!! -- Makefile --------------------------------------------------- !! Runs the code with various compilers. SHELL = bash FC = NO_COMPILER_SPECIFIED COMPILERS = gfortran-7 gfortran-8 ifort PR = @echo$(n)pr -m -t -w 100 define n endef all: rm -rf *.mod *.bin $(foreach FC, $(COMPILERS), $(n)\ rm -rf *.mod && \ $(FC) destructor.f90 -o $(FC).bin && \ chmod +x $(FC).bin) $(PR) $(foreach FC, $(COMPILERS), <(head -1 <($(FC) --version))) $(info) $(foreach N,0 1 2 3 4 5 6,$(n) \ $(PR) $(foreach FC, $(COMPILERS), <(./$(FC).bin $(N)))) !! -- destructor.f90 --------------------------------------------- module mobject implicit none private public tobject, newObject type :: tobject character(32) :: name = "<undef>" contains procedure :: init final :: finalize end type tobject contains subroutine init(object, name) class(tobject), intent(inout) :: object character(*), intent(in) :: name print *, "+ ", name object%name = name end subroutine init function newObject(name) type(tobject) :: newObject character(*), intent(in) :: name call new%init(name) end function newObject subroutine finalize(object) type(tobject) :: object print *, "- ", object%name end subroutine finalize end module mobject module mrun use mobject implicit none contains subroutine run1() type(tobject) :: o1_uninit, o2_field_assigned, o3_tobject, o4_new, o6_init type(tobject), allocatable :: o5_new_alloc, o7_init_alloc print *, ">>>>> run1" o2_field_assigned%name = "o2_field_assigned" o3_tobject = tobject("o3_tobject") o4_new = newObject("o4_new") o5_new_alloc = newObject("o5_new_alloc") call o6_init%init("o6_init") allocate(o7_init_alloc) call o7_init_alloc%init("o7_init_alloc") print *, "<<<<< run1" end subroutine run1 subroutine run2Array() type(tobject) :: objects(4) print *, ">>>>> run2Array" objects(1)%name = "objects(1)_uninit" objects(2) = tobject("objects(2)_tobject") objects(3) = newObject("objects(3)_new") call objects(4)%init("objects(4)_init") print *, "<<<<< run2Array" end subroutine run2Array subroutine run3AllocArr() type(tobject), allocatable :: objects(:) print *, ">>>>> run3AllocArr" allocate(objects(4)) objects(1)%name = "objects(1)_uninit" objects(2) = tobject("objects(2)_tobject") objects(3) = newObject("objects(3)_new") call objects(4)%init("objects(4)_init") print *, "<<<<< run3AllocArr" end subroutine run3AllocArr subroutine run4AllocArrAssgn() type(tobject), allocatable :: objects(:) print *, ">>>>> run4AllocArrAssgn" objects = [ & tobject("objects(1)_tobject"), & newObject("objects(2)_new") ] print *, "<<<<< run4AllocArrAssgn" end subroutine run4AllocArrAssgn subroutine run5MoveAlloc() type(tobject), allocatable :: o_alloc print *, ">>>>> run5MoveAlloc" o_alloc = getAlloc() print *, "<<<<< run5MoveAlloc" end subroutine run5MoveAlloc function getAlloc() result(object) type(tobject), allocatable :: object print *, ">>>>> getAlloc" allocate(object) object = newObject("o_alloc") print *, "<<<<< getAlloc" end function getAlloc subroutine run6MovePtr() type(tobject), pointer :: o_pointer print *, ">>>>> run6MovePtr" o_pointer => getPtr() deallocate(o_pointer) print *, "<<<<< run6MovePtr" end subroutine run6MovePtr function getPtr() result(object) type(tobject), pointer :: object print *, ">>>>> getPtr" allocate(object) object = newObject("o_pointer") print *, "<<<<< getPtr" end function getPtr end module mrun program main use mobject use mrun implicit none type(tobject) :: object character(1) :: argument print *, ">>>>> main" call get_command_argument(1, argument) select case (argument) case("1") call run1() case("2") call run2Array() case("3") call run3AllocArr() case("4") call run4AllocArrAssgn() case("5") call run5MoveAlloc() case("6") call run6MovePtr() case("0") print *, "####################"; print *, ">>>>> runDirectlyInMain" object = newObject("object_in_main") print *, "<<<<< runDirectlyInMain" case default print *, "Incorrect commandline argument" end select print *, "<<<<< main" end program main
测试代码的输出
>> make rm -rf *.mod *.bin rm -rf *.mod && gfortran-7 destructor.f90 -o gfortran-7.bin && chmod +x gfortran-7.bin rm -rf *.mod && gfortran-8 destructor.f90 -o gfortran-8.bin && chmod +x gfortran-8.bin rm -rf *.mod && ifort destructor.f90 -o ifort.bin && chmod +x ifort.bin pr -m -t -w 100 <(head -1 <(gfortran-7 --version)) <(head -1 <(gfortran-8 --version)) <(head -1 <(ifort --version)) GNU Fortran (SUSE Linux) 7.4.0 GNU Fortran (SUSE Linux) 8.2.1 2 ifort (IFORT) 19.0.4.243 2019041 pr -m -t -w 100 <(./gfortran-7.bin 0) <(./gfortran-8.bin 0) <(./ifort.bin 0) >>>>> main >>>>> main >>>>> main #################### #################### #################### >>>>> runDirectlyInMain >>>>> runDirectlyInMain >>>>> runDirectlyInMain + object_in_main + object_in_main + object_in_main <<<<< runDirectlyInMain <<<<< runDirectlyInMain - <undef> <<<<< main <<<<< main - object_in_main <<<<< runDirectlyInMain <<<<< main pr -m -t -w 100 <(./gfortran-7.bin 1) <(./gfortran-8.bin 1) <(./ifort.bin 1) >>>>> main >>>>> main >>>>> main >>>>> run1 >>>>> run1 >>>>> run1 + o4_new + o4_new - <undef> + o5_new_alloc + o5_new_alloc + o4_new + o6_init + o6_init - <undef> + o7_init_alloc + o7_init_alloc - o4_new <<<<< run1 <<<<< run1 + o5_new_alloc - o7_init_alloc - o7_init_alloc - o5_new_alloc - o6_init - o6_init + o6_init - o5_new_alloc - o5_new_alloc + o7_init_alloc - o4_new - o4_new <<<<< run1 - o3_tobject - o3_tobject - <undef> - o2_field_assigned - o2_field_assigned - o2_field_assigned <<<<< main <<<<< main - o3_tobject - o4_new - o6_init - o5_new_alloc - o7_init_alloc <<<<< main pr -m -t -w 100 <(./gfortran-7.bin 2) <(./gfortran-8.bin 2) <(./ifort.bin 2) >>>>> main >>>>> main >>>>> main >>>>> run2Array >>>>> run2Array >>>>> run2Array + objects(3)_new + objects(3)_new - <undef> + objects(4)_init + objects(4)_init + objects(3)_new <<<<< run2Array <<<<< run2Array - <undef> <<<<< main <<<<< main - objects(3)_new + objects(4)_init <<<<< run2Array <<<<< main pr -m -t -w 100 <(./gfortran-7.bin 3) <(./gfortran-8.bin 3) <(./ifort.bin 3) >>>>> main >>>>> main >>>>> main >>>>> run3AllocArr >>>>> run3AllocArr >>>>> run3AllocArr + objects(3)_new + objects(3)_new - <undef> + objects(4)_init + objects(4)_init + objects(3)_new <<<<< run3AllocArr <<<<< run3AllocArr - <undef> <<<<< main <<<<< main - objects(3)_new + objects(4)_init <<<<< run3AllocArr <<<<< main pr -m -t -w 100 <(./gfortran-7.bin 4) <(./gfortran-8.bin 4) <(./ifort.bin 4) >>>>> main >>>>> main >>>>> main >>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn + objects(2)_new + objects(2)_new + objects(2)_new <<<<< run4AllocArrAssgn <<<<< run4AllocArrAssgn - objects(2)_new <<<<< main <<<<< main <<<<< run4AllocArrAssgn <<<<< main pr -m -t -w 100 <(./gfortran-7.bin 5) <(./gfortran-8.bin 5) <(./ifort.bin 5) >>>>> main >>>>> main >>>>> main >>>>> run5MoveAlloc >>>>> run5MoveAlloc >>>>> run5MoveAlloc >>>>> getAlloc >>>>> getAlloc >>>>> getAlloc + o_alloc + o_alloc + o_alloc <<<<< getAlloc <<<<< getAlloc - `4�\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 <<<<< run5MoveAlloc <<<<< run5MoveAlloc - o_alloc - o_alloc - o_alloc <<<<< getAlloc <<<<< main <<<<< main - o_alloc <<<<< run5MoveAlloc - o_alloc <<<<< main pr -m -t -w 100 <(./gfortran-7.bin 6) <(./gfortran-8.bin 6) <(./ifort.bin 6) >>>>> main >>>>> main >>>>> main >>>>> run6MovePtr >>>>> run6MovePtr >>>>> run6MovePtr >>>>> getPtr >>>>> getPtr >>>>> getPtr + o_pointer + o_pointer + o_pointer <<<<< getPtr <<<<< getPtr - `��\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 - o_pointer - o_pointer - o_pointer <<<<< run6MovePtr <<<<< run6MovePtr <<<<< getPtr <<<<< main <<<<< main - o_pointer <<<<< run6MovePtr <<<<< main
现代Fortran包含各种面向对象的思想,包括通过FINAL关键字的“析构函数”概念。模块mobject TYPE :: tobject!数据声明包含最终:::
我担心这个问题是非常主观的,并且将取决于太多因素(您需要支持多个编译器吗?您需要支持其较旧版本吗?到底是哪个版本?如果某些实体未最终确定,这有多重要? )。
您提出了一些主张,如果没有实际的代码示例就很难回答,并且可能是单独的完整问题和解答的主题。 Gfortran在此bug报告https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336中发布了Fortran 2003和2008功能实现的当前状态(该链接指向一个meta bug,该bug指向在bugzilla中跟踪的几个独立问题)。众所周知,该功能尚未完成,并且存在未解决的问题。最值得注意的是(至少对我而言),函数结果尚未最终确定。