Dalam pertanyaan ini: Fungsi Fortran dengan pointer menghasilkan a penugasan normal, dinyatakan bahwa fungsi yang mengembalikan pointer tidak disarankan.

Pertanyaan saya menyangkut konstruktor tipe yang ditentukan pengguna. Perhatikan kode di bawah ini:

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

Dalam kode, saya telah mendefinisikan tipe dengan konstruktor yang mengalokasikan objek dan kemudian mengalokasikan array di objek. Kemudian mengembalikan pointer ke objek.

Jika konstruktor baru saja mengembalikan objek, objek dan array akan disalin dan kemudian dibatalkan alokasinya (setidaknya dengan kompiler yang sesuai standar). Ini dapat menyebabkan overhead dan mengacaukan pelacakan memori kita.

Mengkompilasi kode di atas dengan ifort tidak memberikan peringatan dengan -warn all (kecuali variabel yang tidak digunakan di finalizer) dan kode berperilaku seperti yang saya harapkan. Ini juga berfungsi dengan baik dengan gfortran, kecuali saya mendapat peringatan saat menggunakan -Wall

    TypeObject = PointType(10)
                1
Warning: POINTER-valued function appears on right-hand side of assignment at (1) [-Wsurprising]

Apa risiko menggunakan konstruktor seperti ini? Sejauh yang saya tahu, tidak akan ada pointer yang menggantung dan kita akan memiliki kontrol lebih besar saat objek dialokasikan. Salah satu solusi yang akan mencapai hasil yang sama adalah dengan secara eksplisit mengalokasikan objek dan mengubah konstruktor menjadi subrutin yang menetapkan variabel dan melakukan alokasi array, tetapi terlihat jauh lebih elegan. Apakah ada solusi lain? Kode kami dalam standar Fortran 2008.

2
rolf 3 Maret 2020, 15:29

1 menjawab

Jawaban Terbaik

Jangan gunakan fungsi bernilai pointer. Sebagai aturan, saya tidak pernah membuat fungsi yang mengembalikan fungsi. Mereka buruk dan membingungkan. Mereka menyebabkan bug jahat, terutama ketika salah satu membingungkan => dan =.

Apa fungsinya adalah mengalokasikan objek baru dan membuat pointer yang mengalokasikan objek.

Apa

TypeObject = PointType(10)

Yang dilakukan adalah menyalin nilai dari objek yang disimpan dalam pointer. Kemudian pointer tersebut terlupakan dan memori yang telah ditunjuk oleh pointer tersebut bocor dan hilang selamanya.


Anda menulis "Sejauh yang saya tahu, tidak akan ada pointer yang menggantung dan kita akan memiliki kontrol yang lebih besar ketika objek dialokasikan." Namun, saya tidak melihat cara untuk menghindari pointer yang menggantung dialokasikan di dalam fungsi. Bahkan finalizer tidak dapat membantu di sini. Saya juga tidak melihat bagaimana Anda memiliki kontrol lebih. Memori yang Anda alokasikan secara eksplisit baru saja hilang. Anda memiliki memori yang berbeda untuk TypeObject (kemungkinan pada tumpukan program utama) dan larik di dalam tipe akan dialokasikan lagi selama penyalinan pada penetapan intrinsik TypeObject = PointType(10).

Finalizer dapat menangani komponen array sehingga array yang dialokasikan di dalam fungsi tidak harus hilang. Namun, tipe itu sendiri, yang ditunjuk oleh pointer TypePointer, dengan komponen dan deskriptor non-pointer yang tidak dapat dialokasikan dan sebagainya, tidak dapat dibatalkan alokasinya dari finalizer dan akan tetap menggantung dan memori akan bocor.


Jangan takut dengan fungsi yang mengembalikan objek sebagai nilai. Itu bukan masalah. Kompiler cerdas dan mampu mengoptimalkan salinan yang tidak perlu. Kompilator mungkin dapat dengan mudah mengetahui bahwa Anda baru saja menetapkan hasil fungsi sehingga dapat menggunakan lokasi memori dari target penugasan untuk variabel hasil fungsi (jika tidak harus dapat dialokasikan).

Banyak optimasi lain yang ada.

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

      type(PointType) :: TypePointer

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

Lebih sederhana dan harus bekerja dengan baik. Dengan optimasi bahkan bisa lebih cepat. Jika menggunakan hasil non-pointer yang tidak dapat dialokasikan tidak memungkinkan, gunakan yang dapat dialokasikan. Jangan gunakan pointer untuk hasil fungsi.

1
Vladimir F 3 Maret 2020, 21:35