Fortran构造函数返回指向已分配对象的指针。

3
在这个问题中:Fortran Functions with a pointer result in a normal assignment,指出返回指针的函数不被推荐使用。
我的问题涉及用户定义类型的构造函数。考虑下面的代码:
program PointTest

   use PointMod, only: PointType

   implicit none

   class(PointType), allocatable :: TypeObject

   TypeObject = PointType(10)

end program PointTest

module PointMod

   implicit none

   type PointType

      real(8), dimension(:), allocatable :: array

   contains 

      final :: Finalizer

   end type PointType

   interface PointType

      procedure NewPointType

   end interface PointType


contains


   function NewPointType(n) result(TypePointer)

      implicit none

      integer, intent(in) :: n

      type(PointType), pointer :: TypePointer

      allocate(TypePointer)

      allocate(TypePointer%array(n))

   end function NewPointType


   subroutine Finalizer(this)

      implicit none

      type(PointType) :: this

      print *, 'Finalizer called'

   end subroutine Finalizer


end module PointMod

在代码中,我定义了一个类型,它具有构造函数来分配对象并在对象中分配数组,然后返回指向该对象的指针。
如果构造函数只返回对象,则对象和数组将被复制,然后被释放(至少使用符合标准的编译器)。这可能会导致额外的开销并且会影响我们的内存跟踪。
使用ifort编译上述代码并使用-warn all不会产生警告(最终器中除了未使用的变量)。而且代码表现出了我所期望的行为。在使用-Wall时,使用gfortran编译也能正常工作,但是会产生一个警告。
    TypeObject = PointType(10)
                1
Warning: POINTER-valued function appears on right-hand side of assignment at (1) [-Wsurprising]

使用这样的构造函数有哪些风险?据我所知,不会出现悬挂指针,并且我们将更加控制对象分配的时间。一个实现相同结果的解决方法是显式分配对象并将构造函数转换为子例程来设置变量和数组分配,但这看起来不够优雅。还有其他解决方案吗?我们的代码符合Fortran 2008标准。

如果你担心"如果构造函数只返回对象,那么对象和数组都将被复制"这一点,那么也许值得指出的是,你的代码示例在语句“TypeObject = PointType(10)”中具有内在赋值给TypeObject - francescalus
1个回答

2

不要使用返回指针的函数。通常情况下,我从不制作返回函数的函数。它们很糟糕且令人困惑。它们会导致讨厌的错误,特别是当人们混淆=>=时。

该函数的作用是分配一个新对象并创建一个分配该对象的指针。

什么

TypeObject = PointType(10)

它的意思是,它复制了指针中存储的对象的值。然后指针被遗忘,指向的内存泄漏且永远丢失。


你写道“据我所知,不会有悬空指针的问题,并且我们将更加掌控对象分配的时间。” 然而,我看不到避免函数内部分配悬空指针的方法。即使使用finalizer也无法解决此问题。我也看不出你如何掌控更多。你明确分配的内存仅仅是丢失了。你有一个不同的内存用于TypeObject (可能在主程序的堆栈上),并且在固有赋值TypeObject = PointType(10) 时,类型中的数组将再次分配。

Finalizer可以处理数组组件,这样函数内部分配的数组就不必丢失。但是,指向的指针TypePointer的类型本身,以及其中的非可分配非指针成分、描述符等等,无法从finalizer中释放,将保持悬空状态且内存泄漏。


不要害怕返回对象值的函数,这不是问题。编译器很聪明,能够优化不必要的复制。编译器可以轻松找出你只是将函数结果赋值,因此它可以使用赋值目标的内存位置作为函数结果变量的内存位置(如果不需要可分配)。

存在许多其他优化。

   function NewPointType(n) result(TypePointer)   
      integer, intent(in) :: n

      type(PointType) :: TypePointer

      allocate(TypePointer%array(n))    
   end function NewPointType

这个方法更简单,应该能够正常工作。通过优化,甚至可以更快。如果不能使用非指针和非可分配结果,则使用可分配的方式。不要将指针用于函数结果。


感谢您的快速回答。我以为该函数会返回一个指向相同内存部分的指针副本。在我们的情况下,在运行时我们不知道对象的确切类型,只知道类,因此最好的方法可能是使用非常简单的构造函数和单独的子程序来处理更大的分配。 - rolf
@rolf 这个函数确实返回一个指针。但是,你没有进行指针赋值(=>)。你甚至不能进行指针赋值,因为你的TypeObject没有声明为指针。我建议你再次阅读你在问题中链接的答案。此外,将函数返回的类型分配给类变量并没有什么问题,这取决于使用情况。请注意,在Fortran中,对象本身的内存分配和初始化(包括分配其中的数组组件)是两个完全独立的事情。 - Vladimir F Героям слава

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接