在 Fortran 中分配包含指针的类型实例的正确方法

问题描述 投票:0回答:1

考虑以下最小示例:

module lib
    private
    type node
        integer::val
        type(node),allocatable::next
    end type
    type,public::list
        integer::num=0
        ! `first` should be a pointer to avoid making `this` a target in procedures.
        type(node),pointer::first=>null(),last=>null()
    contains
        procedure::add=>list_add
        final::list_final
    end type
    interface list
        procedure list_new
    end interface
contains
    function list_new()result(this)
        type(list)::this
        print*,'list_new'
        allocate(this%first)
        this%last=>this%first
    end
    subroutine list_add(this,val)
        class(list),intent(inout)::this
        integer,intent(in)::val
        print*,'list_add'
        this%num=this%num+1
        allocate(this%last%next)
        this%last=>this%last%next
        this%last%val=val
    end
    subroutine list_final(this)
        type(list),intent(inout)::this
        print*,'list_final'
        ! Checking if `first` is associated is necessary to avoid the error on
        ! finalization of uninitialized instances.
        if(associated(this%first))deallocate(this%first)
    end
end


program prog
    use lib
    type(list)::obj  ! type(list),allocatable::obj
    obj=list()  ! allocate(obj,source=list())
    call obj%add(42)
end

这是使用可分配节点的链表的简单实现。由于原代码的一些限制,

first
应该是一个指针,在构造函数
list_new
中分配。为了避免内存泄漏,当实例超出范围时,最终过程
list_final
将自动释放
first
。但是编译此代码(例如,使用 GNU 编译器 gfortran-13.1.0 使用
-g
标志进行调试)并执行它将在第
Fortran runtime error: Attempting to allocate already allocated variable 'this'
行以
allocate(this%last%next)
结束。在此错误之前,输出是:

 list_new
 list_final
 list_final
 list_add

如果我理解正确的话,

list_final
被调用了两次:

  • 最终确定第
    type(list)::obj
    ,
  • 行声明中创建的实例
  • 将其内容复制到
    obj=list()
    后,完成在第
    obj
    行右侧创建的实例。

第二个调用释放了

first
list()
所指向的内存,并导致
obj%first
指向无效的内存地址,因为指针刚刚在赋值中被复制。所以
last%next
(或
first%next
)似乎已经在
list_add
的第一次调用中分配了。

type(list)::obj
替换为
type(list),allocatable::obj
并将
obj=list()
替换为
allocate(obj,source=list())
可以修复此问题,并且永远不会调用
list_final

据我所知,这似乎是分配类型实例的唯一安全且干净的方法(无需重新定义赋值并用对析构函数的显式调用替换

final
)。尽管
obj=list()
看起来更优雅并且类似于其他编程语言中的实例初始化表达式,但它可能会在 Fortran 中导致意外行为(并且还会添加额外的复制操作)。

allocate
source
是初始化此类类型实例的唯一正确方法吗?

fortran dynamic-memory-allocation
1个回答
0
投票

通过定义与类型同名的泛型接口,您可以有效地为派生类型定义具有任意语义的构造函数。正确的 Fortran 编译器将调用该通用接口的特定过程,如果其中一个过程与结构构造函数的参数完全匹配,该结构构造函数在语法上也可以是函数调用,并且如果不匹配,则将结构构造函数解释为结构构造函数.

© www.soinside.com 2019 - 2024. All rights reserved.