现代 Fortran 包含各种面向对象的思想,包括通过FINAL
关键字的“析构函数”概念。
MODULE mobject
TYPE :: tobject
! Data declarations
CONTAINS
FINAL :: finalize
END TYPE
CONTAINS
SUBROUTINE finalize(object)
TYPE(tobject) object
...
END SUBROUTINE
END MODULE
然而,这个功能可靠吗?值得注意的是,我注意到有关何时以及是否会调用它的不一致,英特尔 Fortran 19 和 GFortan 7、8 之间存在主要差异:
我注意到 gfortran-7.4.0 和 gfortran-8.2.1.2 之间没有区别。
这些不一致给我带来了一些关于析构函数的实际可用性的问题。是否有任何一种行为完全符合标准?这个标准不清楚吗?标准是否可能包含导致不直观行为的条款?
详分割析(代码见下)
run1
)。 堆栈上未初始化的对象上的
newObject
函数的末尾。 然而,这可以通过明确检查是否
对象在执行任何清理之前被初始化。
这意味着,程序员必须明确检查实例是否已初始化。
allocatable
没有区别。 allocatable
对象或pointer
返回给对象,而是在客户端代码中明确或通过调用释放值时调用它超出allocatable
的范围。这正是我所期望的。 allocatable
而不是 pointer
时,英特尔 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
最佳答案
TLDR:Gfortran 中有一些已知的未决问题。英特尔声称全力支持。一些编译器声称不支持。
一般来说,关于可靠性和可用性的问题是相当主观的,因为你必须考虑许多对你来说独一无二的点(你需要支持多个编译器吗?你需要支持它们的旧版本吗?究竟是哪些?它有多重要如果某个实体尚未最终确定?)。
您提出了一些在没有实际代码示例的情况下难以回答的声明,并且可能是单独的完整问题和答案的主题。 Gfortran 在此错误报告 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336 中发布了 Fortran 2003 和 2008 功能的当前实现状态(该链接指向一个元错误,该元错误指向在 bugzilla 中跟踪的几个单独问题)。众所周知,该功能尚未完成并且存在未决问题。最值得注意的是(至少对我而言),函数结果尚未最终确定。其他编译器的状态概览(简化为 Y/N/paritally)位于 http://fortranwiki.org/fortran/show/Fortran+2003+status,并且曾经在 Fortran 论坛文章中定期更新。
我不能谈论那些所谓的 Intel Fortran 虚假定稿。如果您发现编译器中存在错误,则应向供应商提交错误报告。英特尔通常 react 灵敏。
不过,可以回答一些个别问题。您可能会找到关于它们的单独 Q/As。但:
save
属性。编译器不应该生成任何自动终结。 如果您想详细回答任何要点,您真的必须 提出一个具体问题 。这个太宽泛了。此站点的帮助/说明包含“请编辑问题以将其限制为具有足够详细信息的特定问题,以确定适当的答案。 避免同时提出多个不同的问题 。请参阅 [询问] 以帮助澄清此问题。 ”
关于fortran - Fortran 的 "final"子程序在实际使用中是否足够可靠?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59985499/