1 poin oleh GN⁺ 2024-07-06 | 1 komentar | Bagikan ke WhatsApp

Saya tidak punya konstruktor dan harus diinisialisasi

  • Pendahuluan

    • Saat pertama kali belajar C++, kita mempelajari kapan kompiler menyediakan konstruktor bawaan.
    • Ini menimbulkan pertanyaan tentang risiko objek yang mungkin tidak terinisialisasi dalam situasi tertentu.
  • Inisialisasi bawaan dan inisialisasi nilai

    • T t; melakukan inisialisasi bawaan.
      • Jika T adalah tipe kelas dan memiliki konstruktor bawaan, maka konstruktor itu dijalankan.
      • Jika T adalah tipe array, maka setiap elemennya diinisialisasi secara bawaan.
      • Jika tidak, tidak ada yang dilakukan.
    • T t{}; melakukan inisialisasi nilai.
      • Jika T adalah tipe kelas, maka dilakukan inisialisasi bawaan bila tidak ada konstruktor bawaan atau bila konstruktor bawaan disediakan pengguna atau dihapus.
      • Jika tidak, objek diinisialisasi ke 0 lalu dilakukan inisialisasi bawaan.
      • Jika T adalah tipe array, maka setiap elemennya diinisialisasi sebagai nilai.
      • Jika tidak, objek diinisialisasi ke 0.
  • Konstruktor bawaan

    • Jika tidak mendeklarasikan konstruktor bawaan, kompiler akan mendeklarasikannya secara implisit.
    • Konstruktor bawaan yang dideklarasikan secara implisit memiliki body kosong dan daftar inisialisasi anggota yang kosong.
    • Contoh:
      struct T {
        int x;
        T() = default;
      };
      T t{};
      std::cout << t.x << std::endl; // hasil output adalah 0
      
  • Konstruktor bawaan yang didefinisikan secara implisit

    • Jika konstruktor bawaan dideklarasikan secara implisit atau dideklarasikan secara eksplisit sebagai default, kompiler akan menyediakan konstruktor bawaan yang didefinisikan secara implisit.
    • Contoh:
      struct T {
        T();
      };
      T::T() = default;
      T t{};
      std::cout << t.x << std::endl; // hasil output adalah nilai sampah
      
  • Kasus ketika konstruktor bawaan tidak bisa disediakan

    • Jika T memiliki anggota referensi non-statis
    • Jika T memiliki anggota non-statis atau kelas dasar non-abstrak yang tidak bisa dibangun secara bawaan atau tidak bisa dihancurkan
    • Jika T memiliki anggota non-statis const tanpa inisialisator anggota bawaan
  • Inisialisasi yang benar

    • T t{}; melakukan inisialisasi daftar.
    • Inisialisasi daftar dibagi menjadi inisialisasi daftar langsung dan inisialisasi daftar salin.
    • Contoh:
      struct S {
        int a;
        float b;
        char c;
      };
      S s{3, 4.0f, 'S'}; // tidak ada pemanggilan konstruktor
      
  • Inisialisasi daftar dan inisialisasi agregat

    • Inisialisasi agregat adalah bentuk khusus dari inisialisasi daftar, yang melakukan inisialisasi salin pada setiap elemen kelas atau array dengan elemen yang sesuai dari daftar inisialisasi.
    • Contoh:
      struct A {
        const int x;
      };
      A a{}; // a.x diinisialisasi ke 0
      
  • Inisialisasi dengan tanda kurung

    • Inisialisasi dengan tanda kurung melakukan inisialisasi langsung non-daftar.
    • Contoh:
      struct T {
        const int& r;
      };
      T t(42); // t.r adalah referensi yang menunjuk ke 42
      
  • Ringkasan

    • Aturan inisialisasi memang rumit, tetapi sebagian besar masalah bisa dihindari dengan menulis konstruktor secara langsung.
    • Sebaiknya jangan menyerahkan semuanya pada kompiler; menulis konstruktor sendiri adalah pilihan yang lebih baik.

Opini GN⁺

  • Artikel ini menjelaskan dengan baik kompleksitas aturan inisialisasi di C++.
  • Memahami aturan inisialisasi C++ itu penting karena sangat memengaruhi stabilitas dan performa kode.
  • Menulis konstruktor secara langsung adalah cara terbaik untuk menghindari masalah inisialisasi.
  • Bahasa lain dengan fungsi serupa adalah Rust, dan Rust memiliki aturan inisialisasi yang lebih jelas.
  • Saat mengadopsi teknologi baru, penting untuk memahami dan menggunakan detail seperti aturan inisialisasi dengan baik.

1 komentar

 
GN⁺ 2024-07-06
Komentar Hacker News
  • Hasil inisialisasi t akan menjadi 0

    • Ini karena t mengalami value-initialization, dan karena T tidak memiliki user-defined default constructor, objek di-zero-initialize lalu default constructor dipanggil
  • Default constructor melakukan default-initialization pada anggota, dan ini berbeda dari value-initialization

  • GCC tampaknya setuju dengan hal ini

  • Penulis melewatkan bahwa sebenarnya ia sedang melakukan value-initialization pada x

    • Hasilnya berbeda dari yang diharapkan
  • Detail aturannya rumit dan kadang ada bagian yang tidak masuk akal

    • Namun, dalam kebanyakan kasus Anda bisa mendapatkan hasil yang diharapkan
  • Membuat default-initialization menjadi eksplisit akan menjadi perbaikan besar

    • Karena value-initialization bersifat umum, ketika ingin default-initialization Anda harus menulis komentar
    • Sintaks seperti std::array<int, 100> = void; akan lebih baik
  • Penghubung antara list-initialization dan aggregate initialization adalah bahwa ketika list-initialization dilakukan pada aggregate, maka aggregate initialization yang dijalankan

    • Namun, jika daftar hanya memiliki satu argumen, direct-initialization yang dilakukan
  • Kasus satu elemen bekerja berbeda dari dua elemen atau lebih

    • Ini terjadi dalam bahasa yang makin mempermudah pembuatan struct dari parameter pack
  • Anda bisa menulis constructor sendiri dan menginisialisasi tuple atau array yang hanya diberi satu elemen

    • Namun, dalam kasus khusus constructor yang salah bisa dipanggil
  • Saat initialization list C++11 pertama kali muncul, saya menemukannya dan menganggapnya gila

  • Menyebut "I Have No Mouth, and I Must Scream" (1967)

  • Menggunakan sintaks T::T() = default;

  • Orang berharap hasil output akan menjadi 0, tetapi kenyataannya akan keluar nilai sampah

    • Tidak semua hal bisa sempurna
  • Memungkinkan konsumen library mengubah perilaku library

  • Jika ingin lebih banyak kerumitan C++, C++ FQA direkomendasikan

    • Meski sudah 15 tahun berlalu, ini masih relevan karena C++ hampir tidak pernah menghapus fitur atau perilaku lama
  • Tema blog terinspirasi dari komputer era DEC, tetapi tetap rapi dan minimalis

    • Terasa segar
  • Membaca ini membuat kepala pusing

    • Jadi teringat saat mencoba memahami constructor Java dan inisialisasi objek
  • Go dan Rust tidak punya constructor khusus, sehingga banyak hal jadi lebih sederhana

    • Jadi penasaran apakah ada yang pernah merindukan constructor setelah tidak lagi menggunakannya
  • Penasaran apakah ada alat C++ yang menampilkan semua perilaku implisit

    • Misalnya semua constructor tambahan, implicit copy constructor, dan sebagainya
  • Memberikan informasi yang salah tentang class

    • Jika Anda mendeklarasikan constructor, default constructor tidak disediakan dan default-initialization akan gagal disertai diagnosis dari compiler
  • Klaim bahwa "T t;" berarti "tidak melakukan apa-apa" itu salah

    • Dalam contoh kode, T t; gagal
  • Ada panel depan DEC di header blog