Constructor dan inisialisasi

Nilai bawaan komponen, constructor struktur, dan inisialisasi objek tipe turunan.

Daftar isi

Inisialisasi objek tipe turunan ditangani oleh tiga sarana yang saling melengkapi, yaitu nilai bawaan pada komponen, constructor struktur bawaan bahasa, dan fungsi pabrik yang dibuat sendiri. Topik ini membahas ketiganya beserta aturan alokasi komponen allocatable pada saat constructor dijalankan. Satu modul sensor suhu disertakan dan dijalankan, sehingga perilaku setiap bentuk inisialisasi terlihat dari keluaran aslinya.

Nilai bawaan pada komponen#

Nilai bawaan (default initialization) dinyatakan langsung pada deklarasi komponen dengan tanda sama dengan, misalnya real :: suhu = 0.0. Nilai itu harus berupa ekspresi konstanta yang dapat dihitung pada saat kompilasi, dan berlaku setiap kali sebuah objek tipe tersebut dibuat tanpa nilai eksplisit untuk komponen itu, baik saat variabel dideklarasikan maupun saat objek dibangun oleh constructor. Dengan cara ini komponen yang sering bernilai sama, seperti penghitung yang mulai dari nol, tidak perlu diisi ulang di setiap tempat variabel dibuat.

Komponen yang diberi nilai bawaan pada deklarasi tipe tidak memerlukan penugasan manual setelahnya, sedangkan komponen tanpa nilai bawaan berstatus tidak terdefinisi apabila tidak pernah diisi. Perbedaan ini penting bagi komponen allocatable, karena komponen semacam itu tidak dapat diberi nilai bawaan dan status awalnya selalu tidak teralokasi. Kondisi alokasinya diperiksa dengan fungsi intrinsik allocated() sebelum komponen dibaca atau ditulis.

Constructor struktur bawaan bahasa#

Constructor struktur menghasilkan satu nilai lengkap bertipe turunan dengan menuliskan nama tipe diikuti tanda kurung, misalnya sensor(). Apabila tanda kurung dibiarkan kosong, seluruh komponen memperoleh nilai bawaannya, sedangkan komponen yang diberi nilai dinyatakan mengikuti urutan deklarasi atau dengan menyebut namanya secara eksplisit seperti sensor(id="sensor-ruang", suhu=27.4). Penulisan dengan nama membuat urutan argumen bebas dan maksud setiap nilai terbaca langsung, sehingga disarankan untuk tipe dengan banyak komponen.

Komponen yang tidak disebutkan pada constructor memperoleh nilai bawaannya bila tersedia, dan menjadi tidak terdefinisi bila tidak tersedia. Untuk komponen allocatable, aturannya berbeda, yaitu komponen itu dialokasikan otomatis dengan bentuk sesuai nilai yang diberikan pada constructor, dan tetap tidak teralokasi apabila dilewatkan. Penugasan hasil constructor ke objek yang sudah ada menggantikan seluruh isi lama, termasuk membebaskan alokasi komponen allocatable yang lama sebelum mengisi yang baru.

Constructor tambahan melalui fungsi#

Constructor bawaan bahasa tidak dapat menambahkan logika apa pun, misalnya pemeriksaan masukan atau penyiapan data turunan, sehingga pola yang lazim adalah membuat fungsi pabrik yang mengembalikan objek bertipe tersebut. Fungsi buat_sensor pada modul contoh menerima nama, suhu, dan deret bacaan, lalu mengembalikan objek sensor yang sudah lengkap dengan komponen bacaan teralokasi. Pemanggil c = buat_sensor("sensor-atap", 31.2, [28.5, 29.0, 28.7]) memperoleh objek yang siap pakai tanpa pernah menyentuh komponennya satu per satu.

Pola pabrik ini menjadi wajib ketika ada aturan yang harus dipatuhi setiap kali objek dibuat, misalnya menolak deret kosong atau menghitung nilai rata-rata awal. Aturan yang ditulis di dalam satu fungsi pabrik dipatuhi seluruh program, sedangkan aturan yang hanya ditulis sebagai komentar mudah terlewat di tempat lain. Fortran juga menyediakan mekanisme overload untuk constructor, yaitu antarmuka generik yang bernama sama dengan tipenya, sehingga fungsi pabrik semacam ini boleh dipanggil dengan nama tipe itu sendiri, tetapi contoh pada modul ini sengaja memberi nama sendiri agar perannya sebagai pabrik terlihat jelas.

Program uji inisialisasi#

Program berikut memuat modul data_sensor yang mendeklarasikan tipe sensor dengan tiga nilai bawaan dan satu komponen allocatable, serta fungsi pabrik buat_sensor. Unit program utama kemudian membangun tiga objek dengan tiga cara berbeda untuk membandingkan hasilnya.

fortran constructor.f90
module data_sensor
  implicit none

  type :: sensor
     character(len=16) :: id = "sensor-00"
     real :: suhu = 0.0
     logical :: aktif = .true.
     real, allocatable :: bacaan(:)
  end type sensor

contains

  function buat_sensor(nama_id, suhu, deret) result(s)
    character(len=*), intent(in) :: nama_id
    real, intent(in) :: suhu
    real, intent(in) :: deret(:)
    type(sensor) :: s

    s = sensor(id=nama_id, suhu=suhu, bacaan=deret)
  end function buat_sensor

end module data_sensor

program uji_constructor
  use data_sensor
  implicit none

  type(sensor) :: a, b, c

  a = sensor()
  b = sensor(id="sensor-ruang", suhu=27.4)
  c = buat_sensor("sensor-atap", 31.2, [28.5, 29.0, 28.7])

  print '(1X, A16, 1X, F6.1, 1X, L1)', a%id, a%suhu, a%aktif
  print '(1X, A16, 1X, F6.1, 1X, L1)', b%id, b%suhu, b%aktif
  print '(1X, A16, 1X, F6.1, 1X, L1, 1X, 3F6.1)', c%id, c%suhu, c%aktif, c%bacaan
  print '(1X, A, L1, 1X, A, L1)', "bacaan a terisi:", allocated(a%bacaan), &
       "bacaan c terisi:", allocated(c%bacaan)
end program uji_constructor
bash
# mengompilasi program dari direktori topik
gfortran constructor.f90 -o constructor
# menjalankan program hasil kompilasi
./constructor
text Keluaran
 sensor-00           0.0 T
 sensor-ruang       27.4 T
 sensor-atap        31.2 T   28.5  29.0  28.7
 bacaan a terisi:F bacaan c terisi:T

Keluaran memperlihatkan ketiga jalur inisialisasi secara berurutan. Objek a yang dibangun dengan sensor() seluruhnya mengikuti nilai bawaan, mulai dari id sensor-00, suhu nol, hingga status aktif. Objek b hanya mengganti id dan suhu melalui argumen bernama, sedangkan aktif tetap memperoleh nilai bawaannya. Objek c berasal dari fungsi pabrik dan satu-satunya yang menyimpan deret bacaan, yang dibuktikan oleh baris terakhir, yaitu bacaan milik a tidak teralokasi sedangkan milik c teralokasi.

Kesalahan umum#

Kesalahan pertama adalah membaca komponen allocatable tanpa memeriksa status alokasinya. Pada objek a di atas, pernyataan yang membaca a%bacaan akan gagal pada saat program berjalan karena komponen itu belum dialokasi, dan pesan errornya baru muncul saat eksekusi, bukan saat kompilasi. Kebiasaan yang aman adalah membungkus aksesnya dengan allocated(), sebagaimana dilakukan baris pencetak terakhir pada program contoh.

Kesalahan kedua adalah mengira setiap komponen otomatis bernilai nol atau kosong. Tanpa nilai bawaan pada deklarasi tipe, komponen yang tidak diisi bernilai tak terdefinisi, dan membacanya menghasilkan perilaku yang tidak dapat diandalkan. Kesalahan ketiga adalah menghilangkan satu argumen pada constructor padahal komponen itu tidak punya nilai bawaan, karena constructor tetap diterima compiler dan masalahnya baru terlihat saat program berjalan. Pendekatan yang aman adalah memberi nilai bawaan pada setiap komponen yang punya nilai awal yang wajar.

Ringkasan#

  • Nilai bawaan dinyatakan pada deklarasi komponen dengan ekspresi konstanta, dan berlaku setiap kali objek dibuat tanpa nilai eksplisit untuk komponen tersebut.
  • Constructor struktur menerima argumen berurutan atau bernama, komponen yang dilewatkan mengikuti nilai bawaannya, dan komponen allocatable dialokasikan hanya bila diberi nilai.
  • Fungsi pabrik menggantikan overload constructor untuk membungkus logika pembuatan, dan status komponen allocatable diperiksa dengan allocated() sebelum diakses.

Referensi#